Bab 3: Dari source code ke biner

Daftar Isi Utama

Sebelum memahami bagaimana mengembalikan dari sebuah aplikasi biner, kita perlu memahami dulu bagaimana source code dikompilasi menjadi format biner. Format biner di sini bisa berarti kode mesin yang langsung dijalankan oleh prosessor, ataupun bytecode yang akan dijalankan oleh virtual machine.

Cara kompilasi berbagai bahasa untuk menjadi bentuk biner berbeda. Kode dalam C bisa dikompilasi menjadi executable langsung (EXE di Windows, binary file di Unix/OS lain) dalam kode mesin. Kode dalam bahasa Java akan dikompilasi menjadi .class yang akan dijalankan oleh Java Virtual Machine.

Di mata awam, program seperti py2exe bisa mengubah kode Python menjadi .exe, namun sebenarnya yang terjadi adalah interpreter python dan kode python dimasukkan ke satu file executable. Ketika file exe dijalankan, kode Python ini akan diekstrak -- bisa ke memori, bisa ke disk -- untuk dijalankan.

Untuk memudahkan, saya akan membahas beberapa proses transformasi yang umum bagaimana sebuah source code bisa menjadi file biner/executable. Saya hanya memberikan beberapa contoh saja. Sebaiknya ketika mempelajari sebuah bahasa, Anda mempelajari juga bagaimana proses transformasi (kompilasi, interpretasi) dan eksekusi kode dalam bahasa tersebut.

Kompilasi C ke native code

C merupakan bahasa yang sangat low level. Sangat mudah untuk melihat transformasi dari sebuah source code C menjadi assembly lalu menjadi biner. Biasanya proses transformasi sebuah kode dalam C ke biner adalah sebagai berikut:

  • file dalam bahasa C diolah oleh preprocessor menjadi kode C
  • file dalam bahasa C ditranslasi oleh compiler menjadi assembly
  • kode assembly di-assemble (oleh assembler) menjadi object/machine code
  • object code akan digabung dengan kode dari object code lain dan dengan fungsi library oleh linker menjadi executable

Saya menggunakan GNU Compiler Collection dalam berbagai contoh karena berbagai alasan:

  • Compiler ini berjalan di berbagai sistem operasi (Linux, OS X, dan termasuk juga Windows)
  • Compiler ini mendukung berbagai CPU (misalnya: Intel, ARM, MIPS)
  • Compiler ini gratis
  • Compiler ini open source, kodenya bisa kita lihat

Preprocessor

Preprocessor di C hanya melakukan beberapa hal dasar secara tekstual, tanpa memahami source codenya, hanya sekedar copy paste.

ditaa-preprocessor.png

C ke assembly

Mari kita praktikkan dengan contoh kecil dalam bahasa C. Perhatikan bahwa di sini saya belum akan membahas mengenai kode assembly-nya, hanya ingin menunjukkan beberapa hal yaitu:

  • Compiler bisa diminta untuk menghasilkan assembly saja lalu berhenti
  • Tingkat optimasi compiler bisa diatur
  • Dalam tingkat optimasi tertentu, beberapa hal bisa hilang ketika proses kompilasi dilakukan (karena dioptimasi oleh compiler)
  • Ada berbagai bahasa assembly di dunia ini, tergantung target CPU, bahkan untuk CPU yang sama kadang ada lebih dari satu syntax

ditaa-compiler.png

Untuk berbagai contoh, defaultnya saya memakai memakai Linux AMD64 (Linux 64 bit di platform Intel).

#include <stdio.h>

static int double_it(int x)
{
    return 2*x;
}

int main(int argc, char *argv[])
{
    printf("hello world\n");
    printf("double of 100 is: %d\n", double_it(100));
    return 0;
}

Sengaja saya menggunakan keyword static di fungsi double_it untuk menyatakan bahwa fungsi itu scopenya hanya di file ini saja (dan boleh dioptimasi compiler).

Jika kita hanya ingin melihat translasi menjadi assembly, kita bisa menggunakan:

 gcc -S simple.c

Hasilnya adalah file simple.s. Sebagian string masih bisa terlihat dengan jelas, nama fungsi double_it juga masih terlihat, angka 100 juga masih terlihat. Compiler gcc menghasilkan assembly dalam syntax AT&T.

Kode assembly hanya berbentuk teks biasa sama seperti kode dalam bahasa C, jadi kita bisa menulis sendiri dengan tangan kode assembly, tidak harus dihasilkan dari program C.

SEKALI LAGI: kode listing assembly pada bagian berikut ini belum saatnya untuk dipahami, hanya supaya Anda mendapatkan gambaran berbagai jenis kode assembly yang ada. Banyak pemula bingung ketika diajari assembly Intel lalu berusaha melakukan reverse engineering iOS dan kode assemblynya benar-benar berbeda. Ini juga sekaligus memberikan gambaran betapa berbedanya kode assembly untuk kode C yang sama jika compiler melakukan optimasi.

    .file   "simple.c"
    .text
    .type   double_it, @function
double_it:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movl    -4(%rbp), %eax
    addl    %eax, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   double_it, .-double_it
    .section    .rodata
.LC0:
    .string "hello world"
.LC1:
    .string "double of 100 is: %d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB1:
    .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    %edi, -4(%rbp)
    movq    %rsi, -16(%rbp)
    movl    $.LC0, %edi
    call    puts
    movl    $100, %edi
    call    double_it
    movl    %eax, %esi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size   main, .-main
    .ident  "GCC: (Debian 6.1.1-11) 6.1.1 20160802"
    .section    .note.GNU-stack,"",@progbits

Sebagai selingan: eksperimen ini juga bisa dilakukan di Windows dengan Visual studio

cl /FA /c simple.c

Visual studio menghasilkan listing assembly dalam syntax Intel.

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.23918.0 

include listing.inc

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

_DATA   SEGMENT
$SG4261 DB  'hello world', 0aH, 00H
    ORG $+3
$SG4262 DB  'double of 100 is: %d', 0aH, 00H
_DATA   ENDS
PUBLIC  __local_stdio_printf_options
PUBLIC  _vfprintf_l
PUBLIC  printf
PUBLIC  main
EXTRN   __acrt_iob_func:PROC
EXTRN   __stdio_common_vfprintf:PROC
_DATA   SEGMENT
COMM    ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9:QWORD                            ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_DATA   ENDS
;  COMDAT pdata
pdata   SEGMENT
$pdata$_vfprintf_l DD imagerel $LN3
    DD  imagerel $LN3+67
    DD  imagerel $unwind$_vfprintf_l
pdata   ENDS
;  COMDAT pdata
pdata   SEGMENT
$pdata$printf DD imagerel $LN3
    DD  imagerel $LN3+87
    DD  imagerel $unwind$printf
pdata   ENDS
pdata   SEGMENT
$pdata$main DD  imagerel $LN3
    DD  imagerel $LN3+56
    DD  imagerel $unwind$main
pdata   ENDS
xdata   SEGMENT
$unwind$main DD 010d01H
    DD  0420dH
xdata   ENDS
;  COMDAT xdata
xdata   SEGMENT
$unwind$printf DD 011801H
    DD  06218H
xdata   ENDS
;  COMDAT xdata
xdata   SEGMENT
$unwind$_vfprintf_l DD 011801H
    DD  06218H
xdata   ENDS
; Function compile flags: /Odtp
_TEXT   SEGMENT
argc$ = 48
argv$ = 56
main    PROC
; File c:\gitwork\yohan.es\reverse-engineering\simple.c
; Line 9
$LN3:
    mov QWORD PTR [rsp+16], rdx
    mov DWORD PTR [rsp+8], ecx
    sub rsp, 40                 ; 00000028H
; Line 10
    lea rcx, OFFSET FLAT:$SG4261
    call    printf
; Line 11
    mov ecx, 100                ; 00000064H
    call    double_it
    mov edx, eax
    lea rcx, OFFSET FLAT:$SG4262
    call    printf
; Line 12
    xor eax, eax
; Line 13
    add rsp, 40                 ; 00000028H
    ret 0
main    ENDP
_TEXT   ENDS
; Function compile flags: /Odtp
_TEXT   SEGMENT
x$ = 8
double_it PROC
; File c:\gitwork\yohan.es\reverse-engineering\simple.c
; Line 4
    mov DWORD PTR [rsp+8], ecx
; Line 5
    mov eax, DWORD PTR x$[rsp]
    shl eax, 1
; Line 6
    ret 0
double_it ENDP
_TEXT   ENDS
; Function compile flags: /Odtp
;  COMDAT printf
_TEXT   SEGMENT
_Result$ = 32
_ArgList$ = 40
_Format$ = 64
printf  PROC                        ; COMDAT
; File c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\stdio.h
; Line 950
$LN3:
    mov QWORD PTR [rsp+8], rcx
    mov QWORD PTR [rsp+16], rdx
    mov QWORD PTR [rsp+24], r8
    mov QWORD PTR [rsp+32], r9
    sub rsp, 56                 ; 00000038H
; Line 953
    lea rax, QWORD PTR _Format$[rsp+8]
    mov QWORD PTR _ArgList$[rsp], rax
; Line 954
    mov ecx, 1
    call    __acrt_iob_func
    mov r9, QWORD PTR _ArgList$[rsp]
    xor r8d, r8d
    mov rdx, QWORD PTR _Format$[rsp]
    mov rcx, rax
    call    _vfprintf_l
    mov DWORD PTR _Result$[rsp], eax
; Line 955
    mov QWORD PTR _ArgList$[rsp], 0
; Line 956
    mov eax, DWORD PTR _Result$[rsp]
; Line 957
    add rsp, 56                 ; 00000038H
    ret 0
printf  ENDP
_TEXT   ENDS
; Function compile flags: /Odtp
;  COMDAT _vfprintf_l
_TEXT   SEGMENT
_Stream$ = 64
_Format$ = 72
_Locale$ = 80
_ArgList$ = 88
_vfprintf_l PROC                    ; COMDAT
; File c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\stdio.h
; Line 638
$LN3:
    mov QWORD PTR [rsp+32], r9
    mov QWORD PTR [rsp+24], r8
    mov QWORD PTR [rsp+16], rdx
    mov QWORD PTR [rsp+8], rcx
    sub rsp, 56                 ; 00000038H
; Line 639
    call    __local_stdio_printf_options
    mov rcx, QWORD PTR _ArgList$[rsp]
    mov QWORD PTR [rsp+32], rcx
    mov r9, QWORD PTR _Locale$[rsp]
    mov r8, QWORD PTR _Format$[rsp]
    mov rdx, QWORD PTR _Stream$[rsp]
    mov rcx, QWORD PTR [rax]
    call    __stdio_common_vfprintf
; Line 640
    add rsp, 56                 ; 00000038H
    ret 0
_vfprintf_l ENDP
_TEXT   ENDS
; Function compile flags: /Odtp
;  COMDAT __local_stdio_printf_options
_TEXT   SEGMENT
__local_stdio_printf_options PROC           ; COMDAT
; File c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\corecrt_stdio_config.h
; Line 75
    lea rax, OFFSET FLAT:?_OptionsStorage@?1??__local_stdio_printf_options@@9@9 ; `__local_stdio_printf_options'::`2'::_OptionsStorage
; Line 76
    ret 0
__local_stdio_printf_options ENDP
_TEXT   ENDS
END

Sekarang kembali lagi ke gcc. Sebagai perbandingan, jika kita mengaktifkan optimasi compiler, maka compiler akan menghasilkan kode yang sangat singkat. Di sini, bahkan fungsi double_it sudah hilang, dan langsung digantikan konstanta 200.

 gcc -S -O1 simple.c
    .file   "simple.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "hello world"
.LC1:
    .string "double of 100 is: %d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC0, %edi
    call    puts
    movl    $200, %esi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .ident  "GCC: (Debian 6.1.1-11) 6.1.1 20160802"
    .section    .note.GNU-stack,"",@progbits

Kita bisa menaikkan tingkat optimasi yang dilakukan compiler, dalam contoh ini, sebuah instruksi movl $0, %eax berubah menjadi xorl %eax, %eax. Operasi XOR terhadap suatu bilangan akan menghasilkan 0, ini merupakan optimasi yang sangat low level karena compiler tahu bahwa XOR akan lebih cepat (ini diketahui dari manual CPU).

 gcc -S -O2 simple.c
    .file   "simple.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "hello world"
.LC1:
    .string "double of 100 is: %d\n"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC0, %edi
    call    puts
    movl    $200, %esi
    movl    $.LC1, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .ident  "GCC: (Debian 6.1.1-11) 6.1.1 20160802"
    .section    .note.GNU-stack,"",@progbits

Jika kita coba naikkan lagi tingkat optimasinya, hasilnya tidak berubah karena contoh ini terlalu kecil, di kasus lain, tiap level optimasi bisa menghasilkan kode yang berbeda.

Itu hanyalah contoh kecil assembly dalam AMD64, ini contoh lain dalam ARM.

    .arch armv6
    .eabi_attribute 27, 3
    .eabi_attribute 28, 1
    .fpu vfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 2
    .eabi_attribute 30, 6
    .eabi_attribute 34, 1
    .eabi_attribute 18, 4
    .file   "simple.c"
    .text
    .align  2
    .type   double_it, %function
double_it:
    @ args = 0, pretend = 0, frame = 8
    @ frame_needed = 1, uses_anonymous_args = 0
    @ link register save eliminated.
    str fp, [sp, #-4]!
    add fp, sp, #0
    sub sp, sp, #12
    str r0, [fp, #-8]
    ldr r3, [fp, #-8]
    mov r3, r3, asl #1
    mov r0, r3
    sub sp, fp, #0
    @ sp needed
    ldr fp, [sp], #4
    bx  lr
    .size   double_it, .-double_it
    .section    .rodata
    .align  2
.LC0:
    .ascii  "hello world\000"
    .align  2
.LC1:
    .ascii  "double of 100 is: %d\012\000"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    @ args = 0, pretend = 0, frame = 8
    @ frame_needed = 1, uses_anonymous_args = 0
    stmfd   sp!, {fp, lr}
    add fp, sp, #4
    sub sp, sp, #8
    str r0, [fp, #-8]
    str r1, [fp, #-12]
    ldr r0, .L5
    bl  puts
    mov r0, #100
    bl  double_it
    mov r3, r0
    ldr r0, .L5+4
    mov r1, r3
    bl  printf
    mov r3, #0
    mov r0, r3
    sub sp, fp, #4
    @ sp needed
    ldmfd   sp!, {fp, pc}
.L6:
    .align  2
.L5:
    .word   .LC0
    .word   .LC1
    .size   main, .-main
    .ident  "GCC: (Raspbian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",%progbits

Dan versi ARM yang sudah dioptimasi (O1)

    .arch armv6
    .eabi_attribute 27, 3
    .eabi_attribute 28, 1
    .fpu vfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 2
    .eabi_attribute 30, 1
    .eabi_attribute 34, 1
    .eabi_attribute 18, 4
    .file   "simple.c"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    stmfd   sp!, {r3, lr}
    ldr r0, .L3
    bl  puts
    ldr r0, .L3+4
    mov r1, #200
    bl  printf
    mov r0, #0
    ldmfd   sp!, {r3, pc}
.L4:
    .align  2
.L3:
    .word   .LC0
    .word   .LC1
    .size   main, .-main
    .section    .rodata.str1.4,"aMS",%progbits,1
    .align  2
.LC0:
    .ascii  "hello world\000"
.LC1:
    .ascii  "double of 100 is: %d\012\000"
    .ident  "GCC: (Raspbian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",%progbits

Contoh lain versi MIPS

    .file   1 "simple.c"
    .section .mdebug.abi32
    .previous
    .gnu_attribute 4, 1
    .abicalls
    .text
    .align  2
    .set    nomips16
    .ent    double_it
    .type   double_it, @function
double_it:
    .frame  $fp,8,$31       # vars= 0, regs= 1/0, args= 0, gp= 0
    .mask   0x40000000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .set    nomacro
    
    addiu   $sp,$sp,-8
    sw  $fp,4($sp)
    move    $fp,$sp
    sw  $4,8($fp)
    lw  $2,8($fp)
    nop
    sll $2,$2,1
    move    $sp,$fp
    lw  $fp,4($sp)
    addiu   $sp,$sp,8
    j   $31
    nop

    .set    macro
    .set    reorder
    .end    double_it
    .size   double_it, .-double_it
    .rdata
    .align  2
$LC0:
    .ascii  "hello world\000"
    .align  2
$LC1:
    .ascii  "double of 100 is: %d\012\000"
    .text
    .align  2
    .globl  main
    .set    nomips16
    .ent    main
    .type   main, @function
main:
    .frame  $fp,40,$31      # vars= 0, regs= 3/0, args= 16, gp= 8
    .mask   0xc0010000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .set    nomacro
    
    addiu   $sp,$sp,-40
    sw  $31,36($sp)
    sw  $fp,32($sp)
    sw  $16,28($sp)
    move    $fp,$sp
    lui $28,%hi(__gnu_local_gp)
    addiu   $28,$28,%lo(__gnu_local_gp)
    .cprestore  16
    sw  $4,40($fp)
    sw  $5,44($fp)
    lui $2,%hi($LC0)
    addiu   $4,$2,%lo($LC0)
    lw  $2,%call16(puts)($28)
    nop
    move    $25,$2
    jalr    $25
    nop

    lw  $28,16($fp)
    lui $2,%hi($LC1)
    addiu   $16,$2,%lo($LC1)
    li  $4,100          # 0x64
    .option pic0
    jal double_it
    nop

    .option pic2
    lw  $28,16($fp)
    move    $4,$16
    move    $5,$2
    lw  $2,%call16(printf)($28)
    nop
    move    $25,$2
    jalr    $25
    nop

    lw  $28,16($fp)
    move    $2,$0
    move    $sp,$fp
    lw  $31,36($sp)
    lw  $fp,32($sp)
    lw  $16,28($sp)
    addiu   $sp,$sp,40
    j   $31
    nop

    .set    macro
    .set    reorder
    .end    main
    .size   main, .-main
    .ident  "GCC: (Debian 4.4.5-8) 4.4.5"

Dan yang sudah dioptimasi (O1)

    .file   1 "simple.c"
    .section .mdebug.abi32
    .previous
    .gnu_attribute 4, 1
    .abicalls
    .section    .rodata.str1.4,"aMS",@progbits,1
    .align  2
$LC0:
    .ascii  "hello world\000"
    .align  2
$LC1:
    .ascii  "double of 100 is: %d\012\000"
    .text
    .align  2
    .globl  main
    .set    nomips16
    .ent    main
    .type   main, @function
main:
    .frame  $sp,32,$31      # vars= 0, regs= 1/0, args= 16, gp= 8
    .mask   0x80000000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .set    nomacro
    
    addiu   $sp,$sp,-32
    sw  $31,28($sp)
    lui $28,%hi(__gnu_local_gp)
    addiu   $28,$28,%lo(__gnu_local_gp)
    .cprestore  16
    lui $4,%hi($LC0)
    lw  $25,%call16(puts)($28)
    nop
    jalr    $25
    addiu   $4,$4,%lo($LC0)

    lw  $28,16($sp)
    lui $4,%hi($LC1)
    addiu   $4,$4,%lo($LC1)
    lw  $25,%call16(printf)($28)
    nop
    jalr    $25
    li  $5,200          # 0xc8

    move    $2,$0
    lw  $31,28($sp)
    nop
    j   $31
    addiu   $sp,$sp,32

    .set    macro
    .set    reorder
    .end    main
    .size   main, .-main
    .ident  "GCC: (Debian 4.4.5-8) 4.4.5"

Jika Anda baca sekilas berbagai kode assembly di atas, Anda bisa melihat bahwa: berbagai mnemonik (kode yang bisa dibaca manusia) sangat berbeda antara satu CPU dengan yang lain (misalnya untuk memanggil fungsi: call di Intel, bl di ARM, dan jal di MIPS). Karena penjelasan untuk masing-masing CPU cukup panjang, ini akan dibahas di bab lain.

Assembly ke object code

Listing assembly di bagian sebelumnya masih bisa dibaca manusia. Listing ini bisa diterjemahkan ke bahasa mesin menggunakan assembler. Kita bisa menggunakan program as (assembler) untuk melakukan ini, atau tetap memakai gcc, program gcc ini cukup pintar untuk tahu bahwa jika inputnya adalah .s maka akan otomatis menjalankan program as.

ditaa-assembler.png

Object code adalah kode dalam bahasa mesin, tapi belum lengkap. Misalnya di kode di atas, kita belum tahu bagaimana implementasi printf. Untuk desktop, kemungkinan besar aksinya adalah mencetak ke layar, tapi jika kita membuat program untuk dijalankan di embedded system, mungkin outputnya adalah serial port. Di titik ini kita belum tahu implementasi fungsi tertentu, jadi akan dibiarkan "kosong".

Nanti di proses berikutnya (linking) barulah kita menggabungkan kode yang sudah dihasilkan oleh assembler ini dengan kode pustaka/library.

Sebelum membahas mengenai library, kita teruskan contoh sebelumnya (saya memakai versi yang tidak dioptimasi):

Untuk memanggil assembler:

as simple.s -o simple.o

Atau lebih mudahnya:

gcc -c simple.s

Hasilnya adalah simple.o.

$ file simple.o
simple.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

File ini adalah file biner, ini bisa kita lihat dengan program hexdump atau editor heksadesimal yang lain. Karena cukup panjang, akan saya tunjukkan bagian awalnya saja. Di sini bisa terlihat string "hello world" dan juga "double of 100 is: %d". Bahkan nama fungsi double_it juga bisa dilihat.

Kode bahasa mesin yang dihasilkan memiliki header, yaitu informasi ekstra yang dibutuhkan oleh compiler (perhatikan ini tidak ada hungannya dengan file header berektensi .h ketika memprogram C). Format header ini tergantung pada compiler yang dipakai dan sistem operasi yang dipakai. Di Linux, format yang dipakai adalah ELF (Executable and Linkable Format). Untuk saat ini tidak perlu mengerti dulu format ini. Untuk file ELF, di header file ada teks yang terbaca "ELF", dan biasanya ada juga versi compiler yang membuat file tersebut ("GCC") berikut versinya. Di Windows format yang dipakai adalah MZ/PE dengan ciri-ciri diawali dengan "MZ" dan beberapa puluh byte berikutnya ada "PE").

$ hexdump -C simple.o
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  01 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  00 00 00 00 00 00 00 00  88 03 00 00 00 00 00 00  |................|
00000030  00 00 00 00 40 00 00 00  00 00 40 00 0d 00 0a 00  |....@.....@.....|
00000040  55 48 89 e5 89 7d fc 8b  45 fc 01 c0 5d c3 55 48  |UH...}..E...].UH|
00000050  89 e5 48 83 ec 10 89 7d  fc 48 89 75 f0 bf 00 00  |..H....}.H.u....|
00000060  00 00 e8 00 00 00 00 bf  64 00 00 00 e8 cf ff ff  |........d.......|
00000070  ff 89 c6 bf 00 00 00 00  b8 00 00 00 00 e8 00 00  |................|
00000080  00 00 b8 00 00 00 00 c9  c3 68 65 6c 6c 6f 20 77  |.........hello w|
00000090  6f 72 6c 64 00 64 6f 75  62 6c 65 20 6f 66 20 31  |orld.double of 1|
000000a0  30 30 20 69 73 3a 20 25  64 0a 00 00 47 43 43 3a  |00 is: %d...GCC:|
000000b0  20 28 44 65 62 69 61 6e  20 36 2e 31 2e 31 2d 31  | (Debian 6.1.1-1|
000000c0  31 29 20 36 2e 31 2e 31  20 32 30 31 36 30 38 30  |1) 6.1.1 2016080|
000000d0  32 00 00 00 00 00 00 00  14 00 00 00 00 00 00 00  |2...............|
000000e0  01 7a 52 00 01 78 10 01  1b 0c 07 08 90 01 00 00  |.zR..x..........|
000000f0  1c 00 00 00 1c 00 00 00  00 00 00 00 0e 00 00 00  |................|
00000100  00 41 0e 10 86 02 43 0d  06 49 0c 07 08 00 00 00  |.A....C..I......|
00000110  1c 00 00 00 3c 00 00 00  00 00 00 00 3b 00 00 00  |....<.......;...|
00000120  00 41 0e 10 86 02 43 0d  06 76 0c 07 08 00 00 00  |.A....C..v......|
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000140  00 00 00 00 00 00 00 00  01 00 00 00 04 00 f1 ff  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 03 00 01 00  00 00 00 00 00 00 00 00  |................|
00000170  00 00 00 00 00 00 00 00  00 00 00 00 03 00 03 00  |................|
00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000190  00 00 00 00 03 00 04 00  00 00 00 00 00 00 00 00  |................|
000001a0  00 00 00 00 00 00 00 00  0a 00 00 00 02 00 01 00  |................|
000001b0  00 00 00 00 00 00 00 00  0e 00 00 00 00 00 00 00  |................|
000001c0  00 00 00 00 03 00 05 00  00 00 00 00 00 00 00 00  |................|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 03 00 07 00  |................|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 03 00 08 00  00 00 00 00 00 00 00 00  |................|
00000200  00 00 00 00 00 00 00 00  00 00 00 00 03 00 06 00  |................|
00000210  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000220  14 00 00 00 12 00 01 00  0e 00 00 00 00 00 00 00  |................|
00000230  3b 00 00 00 00 00 00 00  19 00 00 00 10 00 00 00  |;...............|
00000240  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000250  1e 00 00 00 10 00 00 00  00 00 00 00 00 00 00 00  |................|
00000260  00 00 00 00 00 00 00 00  00 73 69 6d 70 6c 65 2e  |.........simple.|
00000270  63 00 64 6f 75 62 6c 65  5f 69 74 00 6d 61 69 6e  |c.double_it.main|
00000280  00 70 75 74 73 00 70 72  69 6e 74 66 00 00 00 00  |.puts.printf....|
00000290  1e 00 00 00 00 00 00 00  0a 00 00 00 06 00 00 00  |................|
000002a0  00 00 00 00 00 00 00 00  23 00 00 00 00 00 00 00  |........#.......|
000002b0  02 00 00 00 0b 00 00 00  fc ff ff ff ff ff ff ff  |................|
000002c0  34 00 00 00 00 00 00 00  0a 00 00 00 06 00 00 00  |4...............|
000002d0  0c 00 00 00 00 00 00 00  3e 00 00 00 00 00 00 00  |........>.......|
000002e0  02 00 00 00 0c 00 00 00  fc ff ff ff ff ff ff ff  |................|
000002f0  20 00 00 00 00 00 00 00  02 00 00 00 02 00 00 00  | ...............|
00000300  00 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000310  02 00 00 00 02 00 00 00  0e 00 00 00 00 00 00 00  |................|
00000320  00 2e 73 79 6d 74 61 62  00 2e 73 74 72 74 61 62  |..symtab..strtab|
00000330  00 2e 73 68 73 74 72 74  61 62 00 2e 72 65 6c 61  |..shstrtab..rela|
00000340  2e 74 65 78 74 00 2e 64  61 74 61 00 2e 62 73 73  |.text..data..bss|
00000350  00 2e 72 6f 64 61 74 61  00 2e 63 6f 6d 6d 65 6e  |..rodata..commen|
00000360  74 00 2e 6e 6f 74 65 2e  47 4e 55 2d 73 74 61 63  |t..note.GNU-stac|
00000370  6b 00 2e 72 65 6c 61 2e  65 68 5f 66 72 61 6d 65  |k..rela.eh_frame|
00000380  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

Format dalam bahasa mesin ini bisa kita kembalikan lagi ke bentuk assembly, tapi tentunya tanpa komentar apapun. Beberapa flag yang saya berikan adalah -a untuk menampikan semua header, -d untuk menampilkan disassembly (ini bisa dilihat di bagian Disassembly of section .text:, -s untuk menampilkan isi section (contents of section), -t untuk menampilkan symbol table (SYMBOL TABLE), dan -r untuk menampilkan informasi relokasi.

Informasi relokasi ini yang nanti akan dipakai oleh linker untuk "mengisi" bagian yang masih belum diketahui saat ini (misalnya puts dan printf).

objdump -a -d -s -t -r simple.o

simple.o:     file format elf64-x86-64
simple.o

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 simple.c
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l     F .text  000000000000000e double_it
0000000000000000 l    d  .rodata    0000000000000000 .rodata
0000000000000000 l    d  .note.GNU-stack    0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame  0000000000000000 .eh_frame
0000000000000000 l    d  .comment   0000000000000000 .comment
000000000000000e g     F .text  000000000000003b main
0000000000000000         *UND*  0000000000000000 puts
0000000000000000         *UND*  0000000000000000 printf


Contents of section .text:
 0000 554889e5 897dfc8b 45fc01c0 5dc35548  UH...}..E...].UH
 0010 89e54883 ec10897d fc488975 f0bf0000  ..H....}.H.u....
 0020 0000e800 000000bf 64000000 e8cfffff  ........d.......
 0030 ff89c6bf 00000000 b8000000 00e80000  ................
 0040 0000b800 000000c9 c3                 .........       
Contents of section .rodata:
 0000 68656c6c 6f20776f 726c6400 646f7562  hello world.doub
 0010 6c65206f 66203130 30206973 3a202564  le of 100 is: %d
 0020 0a00                                 ..              
Contents of section .comment:
 0000 00474343 3a202844 65626961 6e20362e  .GCC: (Debian 6.
 0010 312e312d 31312920 362e312e 31203230  1.1-11) 6.1.1 20
 0020 31363038 303200                      160802.         
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 0e000000 00410e10 8602430d  .........A....C.
 0030 06490c07 08000000 1c000000 3c000000  .I..........<...
 0040 00000000 3b000000 00410e10 8602430d  ....;....A....C.
 0050 06760c07 08000000                    .v......        

Disassembly of section .text:

0000000000000000 <double_it>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   01 c0                   add    %eax,%eax
   c:   5d                      pop    %rbp
   d:   c3                      retq   

000000000000000e <main>:
   e:   55                      push   %rbp
   f:   48 89 e5                mov    %rsp,%rbp
  12:   48 83 ec 10             sub    $0x10,%rsp
  16:   89 7d fc                mov    %edi,-0x4(%rbp)
  19:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
  1d:   bf 00 00 00 00          mov    $0x0,%edi
            1e: R_X86_64_32 .rodata
  22:   e8 00 00 00 00          callq  27 <main+0x19>
            23: R_X86_64_PC32   puts-0x4
  27:   bf 64 00 00 00          mov    $0x64,%edi
  2c:   e8 cf ff ff ff          callq  0 <double_it>
  31:   89 c6                   mov    %eax,%esi
  33:   bf 00 00 00 00          mov    $0x0,%edi
            34: R_X86_64_32 .rodata+0xc
  38:   b8 00 00 00 00          mov    $0x0,%eax
  3d:   e8 00 00 00 00          callq  42 <main+0x34>
            3e: R_X86_64_PC32   printf-0x4
  42:   b8 00 00 00 00          mov    $0x0,%eax
  47:   c9                      leaveq 
  48:   c3                      retq     

Perhatikan juga bagian ini:

 e: 55                      push   %rbp
 f: 48 89 e5                mov    %rsp,%rbp

Di sebelah kiri adalah alamat dalam heksadesimal (e: artinya 0xe atau 14 desimal, f: artinya 0xf atau 15 desimal), lalu di sebelah kananya ada bilangan yang merupakan representasi dalam bahasa mesin, di contoh di atas, 55 adalah push %rbp di AMD64, dan 48 89 e5 adalah mov %rsp,%rbp.

Dari byte-byte bahasa mesin kita bisa mengubahnya menjadi teks assembly, tapi tentunya kita harus tahu arsitektur apa. Serangkaian byte ketika didisassembly dengan arsitektur yang salah akan keluar serangkaian teks assembly yang tidak masuk akal

Jika kita lihat lagi hexdump di atas:

$ hexdump -C simple.o
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  01 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  00 00 00 00 00 00 00 00  88 03 00 00 00 00 00 00  |................|
00000030  00 00 00 00 40 00 00 00  00 00 40 00 0d 00 0a 00  |....@.....@.....|
00000040  55 48 89 e5 89 7d fc 8b  45 fc 01 c0 5d c3 55 48  |UH...}..E...].UH|

Bisa dilihat di baris terakhir ada sederetan angka 55 48 89 e5 89 7d fc ... ini merupakan kode bahasa mesin dalam file tersebut (kode ini yang ada si sebelah kiri push %rbp dst).

Informasi debug

Ada bagian yang saya skip di contoh assembly pertama, yaitu mengenai debugging info. Jika kita mengkompilasi kode dengan opsi -g, kita akan meminta compiler menuliskan informasi debugging. Informasi ini misalnya: di baris berapa di baris asli, apa nama variabel asli, dsb.

gcc -S -g simple.c
gcc -c simple.s

Atau bisa disingkat:

gcc -c -g simple.c

Maka kita akan bisa mendapatkan listing assembly yang disertai dengan source codenya:

objdump -d -S simple.o

simple.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <double_it>:
#include <stdio.h>

static int double_it(int x)
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
    return 2*x;
   7:   8b 45 fc                mov    -0x4(%rbp),%eax
   a:   01 c0                   add    %eax,%eax
}
   c:   5d                      pop    %rbp
   d:   c3                      retq   

000000000000000e <main>:

int main(int argc, char *argv[])
{
   e:   55                      push   %rbp
   f:   48 89 e5                mov    %rsp,%rbp
  12:   48 83 ec 10             sub    $0x10,%rsp
  16:   89 7d fc                mov    %edi,-0x4(%rbp)
  19:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
    printf("hello world\n");
  1d:   bf 00 00 00 00          mov    $0x0,%edi
  22:   e8 00 00 00 00          callq  27 <main+0x19>
    printf("double of 100 is: %d\n", double_it(100));
  27:   bf 64 00 00 00          mov    $0x64,%edi
  2c:   e8 cf ff ff ff          callq  0 <double_it>
  31:   89 c6                   mov    %eax,%esi
  33:   bf 00 00 00 00          mov    $0x0,%edi
  38:   b8 00 00 00 00          mov    $0x0,%eax
  3d:   e8 00 00 00 00          callq  42 <main+0x34>
    return 0;
  42:   b8 00 00 00 00          mov    $0x0,%eax
}
  47:   c9                      leaveq 
  48:   c3                      retq   
    

Linking

Linking bisa dilakukan dengan gcc:

gcc simple.o -o simple  

Atau jika ingin manual juga bisa. Perhatikan bahwa pathnya ini adalah untuk Linux AMD64.

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 simple.o /usr/lib/x86_64-linux-gnu/crtn.o -o simple

ditaa-linker.png

Karena ini bukan tutorial spesifik C, Anda bisa membaca penjelasannya di sini.

Sekarang kita bisa menjalankan hasil executablenya:

$ ./simple
hello world
double of 100 is: 200

File executable ini juga bisa didump dengan objdump, tapi outputnya sekarang akan panjang.

Lihat dump lengkapnya

Perhatikan output yang penting: kita masih bisa melihat kode assembly yang ada di awal. Perhatikan beberapa hal:

  • Alamat yang digunakan sekarang adalah alamat yang sebenarnya (tapi ini tidak sepenuhnya sebenarnya, nanti akan dibahas ketike mempelajari Adress Space Layout Randomization/ASLR)
  • Tidak lagi ada komentar
  • Kita bisa melihat alamat "hello world" ($0x400634) dan "double if 100 is: %d" ($0x400640)
Contents of section .rodata:
 400630 01000200 68656c6c 6f20776f 726c6400  ....hello world.
 400640 646f7562 6c65206f 66203130 30206973  double of 100 is
 400650 3a202564 0a00                        : %d..          


0000000000400566 <double_it>:
  400566:   55                      push   %rbp
  400567:   48 89 e5                mov    %rsp,%rbp
  40056a:   89 7d fc                mov    %edi,-0x4(%rbp)
  40056d:   8b 45 fc                mov    -0x4(%rbp),%eax
  400570:   01 c0                   add    %eax,%eax
  400572:   5d                      pop    %rbp
  400573:   c3                      retq   

0000000000400574 <main>:
  400574:   55                      push   %rbp
  400575:   48 89 e5                mov    %rsp,%rbp
  400578:   48 83 ec 10             sub    $0x10,%rsp
  40057c:   89 7d fc                mov    %edi,-0x4(%rbp)
  40057f:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
  400583:   bf 34 06 40 00          mov    $0x400634,%edi
  400588:   e8 a3 fe ff ff          callq  400430 <puts@plt>
  40058d:   bf 64 00 00 00          mov    $0x64,%edi
  400592:   e8 cf ff ff ff          callq  400566 <double_it>
  400597:   89 c6                   mov    %eax,%esi
  400599:   bf 40 06 40 00          mov    $0x400640,%edi
  40059e:   b8 00 00 00 00          mov    $0x0,%eax
  4005a3:   e8 98 fe ff ff          callq  400440 <printf@plt>
  4005a8:   b8 00 00 00 00          mov    $0x0,%eax
  4005ad:   c9                      leaveq 
  4005ae:   c3                      retq   
  4005af:   90                      nop

Andaikan kita memakai -O2 ketika kompilasi, maka isi main adalah seperti ini (tidak ada lagi double_it):

0000000000400470 <main>:
  400470:   48 83 ec 08             sub    $0x8,%rsp
  400474:   bf 24 06 40 00          mov    $0x400624,%edi
  400479:   e8 b2 ff ff ff          callq  400430 <puts@plt>
  40047e:   be c8 00 00 00          mov    $0xc8,%esi
  400483:   bf 30 06 40 00          mov    $0x400630,%edi
  400488:   31 c0                   xor    %eax,%eax
  40048a:   e8 b1 ff ff ff          callq  400440 <printf@plt>
  40048f:   31 c0                   xor    %eax,%eax
  400491:   48 83 c4 08             add    $0x8,%rsp
  400495:   c3                      retq   
  400496:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)

Kompilasi Pascal ke native code

Saya akan menunjukkan contoh lain bagaimana sebuah bahasa (selain C) dikompilasi menjadi assembly dan executable. Untuk contoh, saya menggunakan Freepascal.

program simple;

function double_it(x :integer ):integer;
begin
   double_it := 2*x;
end;


begin
   writeln('hello world');
   writeln('double of 100 is: ', double_it(100));
end.

Kita bisa membuat listing dengan:

fpc -a simple.pas

Tanpa optimasi, hasil assemblynya seperti ini

    .file "simple.pas"
# Begin asmlist al_procedures

.section .text.n_p$simple_$$_double_it$smallint$$smallint
    .balign 16,0x90
.globl  P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT
    .type   P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT,@function
P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT:
.Lc1:
    pushq   %rbp
.Lc3:
.Lc4:
    movq    %rsp,%rbp
.Lc5:
    leaq    -16(%rsp),%rsp
    movw    %di,-8(%rbp)
    movswl  -8(%rbp),%eax
    shll    $1,%eax
    movw    %ax,-12(%rbp)
    movswl  -12(%rbp),%eax
    leave
    ret
.Lc2:
.Le0:
    .size   P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT, .Le0 - P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT

.section .text.n_main
    .balign 16,0x90
.globl  PASCALMAIN
    .type   PASCALMAIN,@function
PASCALMAIN:
.globl  main
    .type   main,@function
main:
.Lc6:
    pushq   %rbp
.Lc8:
.Lc9:
    movq    %rsp,%rbp
.Lc10:
    leaq    -16(%rsp),%rsp
    movq    %rbx,-8(%rbp)
    call    FPC_INITIALIZEUNITS
    call    fpc_get_output
    movq    %rax,%rbx
    movq    $_$SIMPLE$_Ld1,%rdx
    movq    %rbx,%rsi
    movl    $0,%edi
    call    fpc_write_text_shortstr
    call    FPC_IOCHECK
    movq    %rbx,%rdi
    call    fpc_writeln_end
    call    FPC_IOCHECK
    call    fpc_get_output
    movq    %rax,%rbx
    movq    $_$SIMPLE$_Ld2,%rdx
    movq    %rbx,%rsi
    movl    $0,%edi
    call    fpc_write_text_shortstr
    call    FPC_IOCHECK
    movl    $100,%edi
    call    P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT
    movw    %ax,%dx
    movswq  %dx,%rdx
    movq    %rbx,%rsi
    movl    $0,%edi
    call    fpc_write_text_sint
    call    FPC_IOCHECK
    movq    %rbx,%rdi
    call    fpc_writeln_end
    call    FPC_IOCHECK
    call    FPC_DO_EXIT
    movq    -8(%rbp),%rbx
    leave
    ret
.Lc7:
.Le1:
    .size   main, .Le1 - main

.section .text
# End asmlist al_procedures
# Begin asmlist al_globals

.section .data.n_INITFINAL
    .balign 8
.globl  INITFINAL
    .type   INITFINAL,@object
INITFINAL:
    .quad   1,0
    .quad   INIT$_$SYSTEM
    .quad   0
.Le2:
    .size   INITFINAL, .Le2 - INITFINAL

.section .data.n_FPC_THREADVARTABLES
    .balign 8
.globl  FPC_THREADVARTABLES
    .type   FPC_THREADVARTABLES,@object
FPC_THREADVARTABLES:
    .long   1
    .quad   THREADVARLIST_$SYSTEM
.Le3:
    .size   FPC_THREADVARTABLES, .Le3 - FPC_THREADVARTABLES

.section .data.n_FPC_RESOURCESTRINGTABLES
    .balign 8
.globl  FPC_RESOURCESTRINGTABLES
    .type   FPC_RESOURCESTRINGTABLES,@object
FPC_RESOURCESTRINGTABLES:
    .quad   0
.Le4:
    .size   FPC_RESOURCESTRINGTABLES, .Le4 - FPC_RESOURCESTRINGTABLES

.section .data.n_FPC_WIDEINITTABLES
    .balign 8
.globl  FPC_WIDEINITTABLES
    .type   FPC_WIDEINITTABLES,@object
FPC_WIDEINITTABLES:
    .quad   0
.Le5:
    .size   FPC_WIDEINITTABLES, .Le5 - FPC_WIDEINITTABLES

.section .data.n_FPC_RESSTRINITTABLES
    .balign 8
.globl  FPC_RESSTRINITTABLES
    .type   FPC_RESSTRINITTABLES,@object
FPC_RESSTRINITTABLES:
    .quad   0
.Le6:
    .size   FPC_RESSTRINITTABLES, .Le6 - FPC_RESSTRINITTABLES

.section .fpc.n_version
    .balign 8
    .ascii  "FPC 3.0.0+dfsg-8 [2016/09/03] for x86_64 - Linux"

.section .data.n___stklen
    .balign 8
.globl  __stklen
    .type   __stklen,@object
__stklen:
    .quad   8388608

.section .data.n___heapsize
    .balign 8
.globl  __heapsize
    .type   __heapsize,@object
__heapsize:
    .quad   0

.section .data.n___fpc_valgrind
.globl  __fpc_valgrind
    .type   __fpc_valgrind,@object
__fpc_valgrind:
    .byte   0

.section .data.n_FPC_RESLOCATION
    .balign 8
.globl  FPC_RESLOCATION
    .type   FPC_RESLOCATION,@object
FPC_RESLOCATION:
    .quad   0
# End asmlist al_globals
# Begin asmlist al_typedconsts

.section .rodata.n__$SIMPLE$_Ld1
    .balign 8
.globl  _$SIMPLE$_Ld1
_$SIMPLE$_Ld1:
    .ascii  "\013hello world\000"

.section .rodata.n__$SIMPLE$_Ld2
    .balign 8
.globl  _$SIMPLE$_Ld2
_$SIMPLE$_Ld2:
    .ascii  "\022double of 100 is: \000"
# End asmlist al_typedconsts
# Begin asmlist al_dwarf_frame

.section .debug_frame
.Lc11:
    .long   .Lc13-.Lc12
.Lc12:
    .long   -1
    .byte   1
    .byte   0
    .uleb128    1
    .sleb128    -4
    .byte   16
    .byte   12
    .uleb128    7
    .uleb128    8
    .byte   5
    .uleb128    16
    .uleb128    2
    .balign 4,0
.Lc13:
    .long   .Lc15-.Lc14
.Lc14:
    .quad   .Lc11
    .quad   .Lc1
    .quad   .Lc2-.Lc1
    .byte   4
    .long   .Lc3-.Lc1
    .byte   14
    .uleb128    16
    .byte   4
    .long   .Lc4-.Lc3
    .byte   5
    .uleb128    6
    .uleb128    4
    .byte   4
    .long   .Lc5-.Lc4
    .byte   13
    .uleb128    6
    .balign 4,0
.Lc15:
    .long   .Lc17-.Lc16
.Lc16:
    .quad   .Lc11
    .quad   .Lc6
    .quad   .Lc7-.Lc6
    .byte   4
    .long   .Lc8-.Lc6
    .byte   14
    .uleb128    16
    .byte   4
    .long   .Lc9-.Lc8
    .byte   5
    .uleb128    6
    .uleb128    4
    .byte   4
    .long   .Lc10-.Lc9
    .byte   13
    .uleb128    6
    .balign 4,0
.Lc17:
# End asmlist al_dwarf_frame
.section .note.GNU-stack,"",%progbits

Dengan optimasi:

fpc -a -O4 simple.pas
    .file "simple.pas"
# Begin asmlist al_procedures

.section .text.n_p$simple_$$_double_it$smallint$$smallint
    .balign 16,0x90
.globl  P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT
    .type   P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT,@function
P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT:
.Lc1:
    pushq   %rbp
.Lc3:
.Lc4:
    movq    %rsp,%rbp
.Lc5:
    leaq    -16(%rsp),%rsp
    movw    %di,-8(%rbp)
    movswl  -8(%rbp),%eax
    shll    $1,%eax
    movw    %ax,-12(%rbp)
    movswl  -12(%rbp),%eax
    leave
    ret
.Lc2:
.Le0:
    .size   P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT, .Le0 - P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT

.section .text.n_main
    .balign 16,0x90
.globl  PASCALMAIN
    .type   PASCALMAIN,@function
PASCALMAIN:
.globl  main
    .type   main,@function
main:
.Lc6:
    pushq   %rbp
.Lc8:
.Lc9:
    movq    %rsp,%rbp
.Lc10:
    leaq    -16(%rsp),%rsp
    movq    %rbx,-8(%rbp)
    call    FPC_INITIALIZEUNITS
    call    fpc_get_output
    movq    %rax,%rbx
    movq    $_$SIMPLE$_Ld1,%rdx
    movq    %rbx,%rsi
    movl    $0,%edi
    call    fpc_write_text_shortstr
    call    FPC_IOCHECK
    movq    %rbx,%rdi
    call    fpc_writeln_end
    call    FPC_IOCHECK
    call    fpc_get_output
    movq    %rax,%rbx
    movq    $_$SIMPLE$_Ld2,%rdx
    movq    %rbx,%rsi
    movl    $0,%edi
    call    fpc_write_text_shortstr
    call    FPC_IOCHECK
    movl    $100,%edi
    call    P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT
    movw    %ax,%dx
    movswq  %dx,%rdx
    movq    %rbx,%rsi
    movl    $0,%edi
    call    fpc_write_text_sint
    call    FPC_IOCHECK
    movq    %rbx,%rdi
    call    fpc_writeln_end
    call    FPC_IOCHECK
    call    FPC_DO_EXIT
    movq    -8(%rbp),%rbx
    leave
    ret
.Lc7:
.Le1:
    .size   main, .Le1 - main

.section .text
# End asmlist al_procedures
# Begin asmlist al_globals

.section .data.n_INITFINAL
    .balign 8
.globl  INITFINAL
    .type   INITFINAL,@object
INITFINAL:
    .quad   1,0
    .quad   INIT$_$SYSTEM
    .quad   0
.Le2:
    .size   INITFINAL, .Le2 - INITFINAL

.section .data.n_FPC_THREADVARTABLES
    .balign 8
.globl  FPC_THREADVARTABLES
    .type   FPC_THREADVARTABLES,@object
FPC_THREADVARTABLES:
    .long   1
    .quad   THREADVARLIST_$SYSTEM
.Le3:
    .size   FPC_THREADVARTABLES, .Le3 - FPC_THREADVARTABLES

.section .data.n_FPC_RESOURCESTRINGTABLES
    .balign 8
.globl  FPC_RESOURCESTRINGTABLES
    .type   FPC_RESOURCESTRINGTABLES,@object
FPC_RESOURCESTRINGTABLES:
    .quad   0
.Le4:
    .size   FPC_RESOURCESTRINGTABLES, .Le4 - FPC_RESOURCESTRINGTABLES

.section .data.n_FPC_WIDEINITTABLES
    .balign 8
.globl  FPC_WIDEINITTABLES
    .type   FPC_WIDEINITTABLES,@object
FPC_WIDEINITTABLES:
    .quad   0
.Le5:
    .size   FPC_WIDEINITTABLES, .Le5 - FPC_WIDEINITTABLES

.section .data.n_FPC_RESSTRINITTABLES
    .balign 8
.globl  FPC_RESSTRINITTABLES
    .type   FPC_RESSTRINITTABLES,@object
FPC_RESSTRINITTABLES:
    .quad   0
.Le6:
    .size   FPC_RESSTRINITTABLES, .Le6 - FPC_RESSTRINITTABLES

.section .fpc.n_version
    .balign 8
    .ascii  "FPC 3.0.0+dfsg-8 [2016/09/03] for x86_64 - Linux"

.section .data.n___stklen
    .balign 8
.globl  __stklen
    .type   __stklen,@object
__stklen:
    .quad   8388608

.section .data.n___heapsize
    .balign 8
.globl  __heapsize
    .type   __heapsize,@object
__heapsize:
    .quad   0

.section .data.n___fpc_valgrind
.globl  __fpc_valgrind
    .type   __fpc_valgrind,@object
__fpc_valgrind:
    .byte   0

.section .data.n_FPC_RESLOCATION
    .balign 8
.globl  FPC_RESLOCATION
    .type   FPC_RESLOCATION,@object
FPC_RESLOCATION:
    .quad   0
# End asmlist al_globals
# Begin asmlist al_typedconsts

.section .rodata.n__$SIMPLE$_Ld1
    .balign 8
.globl  _$SIMPLE$_Ld1
_$SIMPLE$_Ld1:
    .ascii  "\013hello world\000"

.section .rodata.n__$SIMPLE$_Ld2
    .balign 8
.globl  _$SIMPLE$_Ld2
_$SIMPLE$_Ld2:
    .ascii  "\022double of 100 is: \000"
# End asmlist al_typedconsts
# Begin asmlist al_dwarf_frame

.section .debug_frame
.Lc11:
    .long   .Lc13-.Lc12
.Lc12:
    .long   -1
    .byte   1
    .byte   0
    .uleb128    1
    .sleb128    -4
    .byte   16
    .byte   12
    .uleb128    7
    .uleb128    8
    .byte   5
    .uleb128    16
    .uleb128    2
    .balign 4,0
.Lc13:
    .long   .Lc15-.Lc14
.Lc14:
    .quad   .Lc11
    .quad   .Lc1
    .quad   .Lc2-.Lc1
    .byte   4
    .long   .Lc3-.Lc1
    .byte   14
    .uleb128    16
    .byte   4
    .long   .Lc4-.Lc3
    .byte   5
    .uleb128    6
    .uleb128    4
    .byte   4
    .long   .Lc5-.Lc4
    .byte   13
    .uleb128    6
    .balign 4,0
.Lc15:
    .long   .Lc17-.Lc16
.Lc16:
    .quad   .Lc11
    .quad   .Lc6
    .quad   .Lc7-.Lc6
    .byte   4
    .long   .Lc8-.Lc6
    .byte   14
    .uleb128    16
    .byte   4
    .long   .Lc9-.Lc8
    .byte   5
    .uleb128    6
    .uleb128    4
    .byte   4
    .long   .Lc10-.Lc9
    .byte   13
    .uleb128    6
    .balign 4,0
.Lc17:
# End asmlist al_dwarf_frame
.section .note.GNU-stack,"",%progbits

Kita bisa melihat assembly setelah menjadi executable. Secara default, simbol tidak akan dihasilkan, jadi perlu ditambahkan opsi -g.

fpc -g -O4 simple.pas
    

00000000004001c0 <P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT>:
  4001c0:   66 89 f8                mov    %di,%ax
  4001c3:   0f bf c0                movswl %ax,%eax
  4001c6:   d1 e0                   shl    %eax
  4001c8:   0f bf c0                movswl %ax,%eax
  4001cb:   c3                      retq   
  
00000000004001d0 <PASCALMAIN>:
  4001d0:   53                      push   %rbx
  4001d1:   e8 9a 66 01 00          callq  416870 <FPC_INITIALIZEUNITS>
  4001d6:   e8 75 c0 01 00          callq  41c250 <fpc_get_output>
  4001db:   48 89 c3                mov    %rax,%rbx
  4001de:   48 ba c0 2c 42 00 00    movabs $0x422cc0,%rdx
  4001e5:   00 00 00 
  4001e8:   48 89 de                mov    %rbx,%rsi
  4001eb:   bf 00 00 00 00          mov    $0x0,%edi
  4001f0:   e8 0b c3 01 00          callq  41c500 <FPC_WRITE_TEXT_SHORTSTR>
  4001f5:   e8 d6 64 01 00          callq  4166d0 <FPC_IOCHECK>
  4001fa:   48 89 df                mov    %rbx,%rdi
  4001fd:   e8 2e c2 01 00          callq  41c430 <fpc_writeln_end>
  400202:   e8 c9 64 01 00          callq  4166d0 <FPC_IOCHECK>
  400207:   e8 44 c0 01 00          callq  41c250 <fpc_get_output>
  40020c:   48 89 c3                mov    %rax,%rbx
  40020f:   48 ba d0 2c 42 00 00    movabs $0x422cd0,%rdx
  400216:   00 00 00 
  400219:   48 89 de                mov    %rbx,%rsi
  40021c:   bf 00 00 00 00          mov    $0x0,%edi
  400221:   e8 da c2 01 00          callq  41c500 <FPC_WRITE_TEXT_SHORTSTR>
  400226:   e8 a5 64 01 00          callq  4166d0 <FPC_IOCHECK>
  40022b:   bf 64 00 00 00          mov    $0x64,%edi
  400230:   e8 8b ff ff ff          callq  4001c0 <P$SIMPLE_$$_DOUBLE_IT$SMALLINT$$SMALLINT>
  400235:   66 89 c2                mov    %ax,%dx
  400238:   48 0f bf d2             movswq %dx,%rdx
  40023c:   48 89 de                mov    %rbx,%rsi
  40023f:   bf 00 00 00 00          mov    $0x0,%edi
  400244:   e8 e7 cb 01 00          callq  41ce30 <fpc_write_text_sint>
  400249:   e8 82 64 01 00          callq  4166d0 <FPC_IOCHECK>
  40024e:   48 89 df                mov    %rbx,%rdi
  400251:   e8 da c1 01 00          callq  41c430 <fpc_writeln_end>
  400256:   e8 75 64 01 00          callq  4166d0 <FPC_IOCHECK>
  40025b:   e8 b0 6a 01 00          callq  416d10 <FPC_DO_EXIT>
  400260:   5b                      pop    %rbx
  400261:   c3                      retq   
  

Meskipun sudah memakai tingkat optimasi maksimum, kode assembly yang dihasilkan oleh FPC masih cukup panjang.

Kompilasi Go ke native

Go adalah contoh lain bahasa yang dikompilasi ke native code.

package main

import "fmt"

func double_it(x int) int {
    return 2*x
}

func main() {
    fmt.Println("hello world")
    fmt.Println("double of 100 is: ", double_it(100))
}
go tool compile -S  simple.go

Kode yang dihasilkan cukup panjang, jadi tidak akan saya tampilkan seluruhnya

Disassembly of section .text:

0000000000401000 <main.main>:
  401000:   64 48 8b 0c 25 f8 ff    mov    %fs:0xfffffffffffffff8,%rcx
  401007:   ff ff 
  401009:   48 8d 44 24 f0          lea    -0x10(%rsp),%rax
  40100e:   48 3b 41 10             cmp    0x10(%rcx),%rax
  401012:   0f 86 7b 01 00 00       jbe    401193 <main.main+0x193>
  401018:   48 81 ec 90 00 00 00    sub    $0x90,%rsp
  40101f:   48 89 ac 24 88 00 00    mov    %rbp,0x88(%rsp)
  401026:   00 
  401027:   48 8d ac 24 88 00 00    lea    0x88(%rsp),%rbp
  40102e:   00 
  40102f:   48 8d 05 8b 48 0a 00    lea    0xa488b(%rip),%rax        # 4a58c1 <go.string.*+0xb11>
  401036:   48 89 44 24 58          mov    %rax,0x58(%rsp)
  40103b:   48 c7 44 24 60 0b 00    movq   $0xb,0x60(%rsp)
  401042:   00 00 
  401044:   48 c7 44 24 38 00 00    movq   $0x0,0x38(%rsp)
  40104b:   00 00 
  40104d:   48 c7 44 24 40 00 00    movq   $0x0,0x40(%rsp)
  401054:   00 00 
  401056:   48 8d 05 83 95 08 00    lea    0x89583(%rip),%rax        # 48a5e0 <type.*+0xd5e0>
  40105d:   48 89 04 24             mov    %rax,(%rsp)
  401061:   48 8d 4c 24 58          lea    0x58(%rsp),%rcx
  401066:   48 89 4c 24 08          mov    %rcx,0x8(%rsp)
  40106b:   48 c7 44 24 10 00 00    movq   $0x0,0x10(%rsp)
  401072:   00 00 
  401074:   e8 97 9f 00 00          callq  40b010 <runtime.convT2E>
  401079:   48 8b 44 24 20          mov    0x20(%rsp),%rax
  40107e:   48 8b 4c 24 18          mov    0x18(%rsp),%rcx
  401083:   48 89 4c 24 38          mov    %rcx,0x38(%rsp)
  401088:   48 89 44 24 40          mov    %rax,0x40(%rsp)
  40108d:   48 8d 44 24 38          lea    0x38(%rsp),%rax
  401092:   48 89 04 24             mov    %rax,(%rsp)
  401096:   48 c7 44 24 08 01 00    movq   $0x1,0x8(%rsp)
  40109d:   00 00 
  40109f:   48 c7 44 24 10 01 00    movq   $0x1,0x10(%rsp)
  4010a6:   00 00 
  4010a8:   e8 d3 45 05 00          callq  455680 <fmt.Println>
  4010ad:   48 8d 05 1e 55 0a 00    lea    0xa551e(%rip),%rax        # 4a65d2 <go.string.*+0x1822>
  4010b4:   48 89 44 24 48          mov    %rax,0x48(%rsp)
  4010b9:   48 c7 44 24 50 12 00    movq   $0x12,0x50(%rsp)
  4010c0:   00 00 
  4010c2:   48 c7 44 24 30 c8 00    movq   $0xc8,0x30(%rsp)
  4010c9:   00 00 
  4010cb:   48 c7 44 24 68 00 00    movq   $0x0,0x68(%rsp)
  4010d2:   00 00 
  4010d4:   48 c7 44 24 70 00 00    movq   $0x0,0x70(%rsp)
  4010db:   00 00 
  4010dd:   48 c7 44 24 78 00 00    movq   $0x0,0x78(%rsp)
  4010e4:   00 00 
  4010e6:   48 c7 84 24 80 00 00    movq   $0x0,0x80(%rsp)
  4010ed:   00 00 00 00 00 
  4010f2:   48 8d 05 e7 94 08 00    lea    0x894e7(%rip),%rax        # 48a5e0 <type.*+0xd5e0>
  4010f9:   48 89 04 24             mov    %rax,(%rsp)
  4010fd:   48 8d 44 24 48          lea    0x48(%rsp),%rax
  401102:   48 89 44 24 08          mov    %rax,0x8(%rsp)
  401107:   48 c7 44 24 10 00 00    movq   $0x0,0x10(%rsp)
  40110e:   00 00 
  401110:   e8 fb 9e 00 00          callq  40b010 <runtime.convT2E>
  401115:   48 8b 44 24 18          mov    0x18(%rsp),%rax
  40111a:   48 8b 4c 24 20          mov    0x20(%rsp),%rcx
  40111f:   48 89 44 24 68          mov    %rax,0x68(%rsp)
  401124:   48 89 4c 24 70          mov    %rcx,0x70(%rsp)
  401129:   48 8d 05 f0 8f 08 00    lea    0x88ff0(%rip),%rax        # 48a120 <type.*+0xd120>
  401130:   48 89 04 24             mov    %rax,(%rsp)
  401134:   48 8d 44 24 30          lea    0x30(%rsp),%rax
  401139:   48 89 44 24 08          mov    %rax,0x8(%rsp)
  40113e:   48 c7 44 24 10 00 00    movq   $0x0,0x10(%rsp)
  401145:   00 00 
  401147:   e8 c4 9e 00 00          callq  40b010 <runtime.convT2E>
  40114c:   48 8b 44 24 20          mov    0x20(%rsp),%rax
  401151:   48 8b 4c 24 18          mov    0x18(%rsp),%rcx
  401156:   48 89 4c 24 78          mov    %rcx,0x78(%rsp)
  40115b:   48 89 84 24 80 00 00    mov    %rax,0x80(%rsp)
  401162:   00 
  401163:   48 8d 44 24 68          lea    0x68(%rsp),%rax
  401168:   48 89 04 24             mov    %rax,(%rsp)
  40116c:   48 c7 44 24 08 02 00    movq   $0x2,0x8(%rsp)
  401173:   00 00 
  401175:   48 c7 44 24 10 02 00    movq   $0x2,0x10(%rsp)
  40117c:   00 00 
  40117e:   e8 fd 44 05 00          callq  455680 <fmt.Println>
  401183:   48 8b ac 24 88 00 00    mov    0x88(%rsp),%rbp
  40118a:   00 
  40118b:   48 81 c4 90 00 00 00    add    $0x90,%rsp
  401192:   c3                      retq   
    

Kompilasi Java ke .class

Contoh untuk Java saya samakan dengan C:

Kompilasi

class Simple {

    private static int doubleIt(int d) {
        return d*2;
    }

    public static void main(String argv[]) {
        System.out.println("hello world");
        System.out.println("double of 100 is: " + doubleIt(100));     
    }
    
}
javac Simple.java

Hasilnya adalah langsung sebuah file .class.

hexdump -C Simple.class
00000000  ca fe ba be 00 00 00 34  00 31 0a 00 0d 00 18 09  |.......4.1......|
00000010  00 19 00 1a 08 00 1b 0a  00 1c 00 1d 07 00 1e 0a  |................|
00000020  00 05 00 18 08 00 1f 0a  00 05 00 20 0a 00 0c 00  |........... ....|
00000030  21 0a 00 05 00 22 0a 00  05 00 23 07 00 24 07 00  |!...."....#..$..|
00000040  25 01 00 06 3c 69 6e 69  74 3e 01 00 03 28 29 56  |%...<init>...()V|
00000050  01 00 04 43 6f 64 65 01  00 0f 4c 69 6e 65 4e 75  |...Code...LineNu|
00000060  6d 62 65 72 54 61 62 6c  65 01 00 08 64 6f 75 62  |mberTable...doub|
00000070  6c 65 49 74 01 00 04 28  49 29 49 01 00 04 6d 61  |leIt...(I)I...ma|
00000080  69 6e 01 00 16 28 5b 4c  6a 61 76 61 2f 6c 61 6e  |in...([Ljava/lan|
00000090  67 2f 53 74 72 69 6e 67  3b 29 56 01 00 0a 53 6f  |g/String;)V...So|
000000a0  75 72 63 65 46 69 6c 65  01 00 0b 53 69 6d 70 6c  |urceFile...Simpl|
000000b0  65 2e 6a 61 76 61 0c 00  0e 00 0f 07 00 26 0c 00  |e.java.......&..|
000000c0  27 00 28 01 00 0b 68 65  6c 6c 6f 20 77 6f 72 6c  |'.(...hello worl|
000000d0  64 07 00 29 0c 00 2a 00  2b 01 00 17 6a 61 76 61  |d..)..*.+...java|
000000e0  2f 6c 61 6e 67 2f 53 74  72 69 6e 67 42 75 69 6c  |/lang/StringBuil|
000000f0  64 65 72 01 00 12 64 6f  75 62 6c 65 20 6f 66 20  |der...double of |
00000100  31 30 30 20 69 73 3a 20  0c 00 2c 00 2d 0c 00 12  |100 is: ..,.-...|
00000110  00 13 0c 00 2c 00 2e 0c  00 2f 00 30 01 00 06 53  |....,..../.0...S|
00000120  69 6d 70 6c 65 01 00 10  6a 61 76 61 2f 6c 61 6e  |imple...java/lan|
00000130  67 2f 4f 62 6a 65 63 74  01 00 10 6a 61 76 61 2f  |g/Object...java/|
00000140  6c 61 6e 67 2f 53 79 73  74 65 6d 01 00 03 6f 75  |lang/System...ou|
00000150  74 01 00 15 4c 6a 61 76  61 2f 69 6f 2f 50 72 69  |t...Ljava/io/Pri|
00000160  6e 74 53 74 72 65 61 6d  3b 01 00 13 6a 61 76 61  |ntStream;...java|
00000170  2f 69 6f 2f 50 72 69 6e  74 53 74 72 65 61 6d 01  |/io/PrintStream.|
00000180  00 07 70 72 69 6e 74 6c  6e 01 00 15 28 4c 6a 61  |..println...(Lja|
00000190  76 61 2f 6c 61 6e 67 2f  53 74 72 69 6e 67 3b 29  |va/lang/String;)|
000001a0  56 01 00 06 61 70 70 65  6e 64 01 00 2d 28 4c 6a  |V...append..-(Lj|
000001b0  61 76 61 2f 6c 61 6e 67  2f 53 74 72 69 6e 67 3b  |ava/lang/String;|
000001c0  29 4c 6a 61 76 61 2f 6c  61 6e 67 2f 53 74 72 69  |)Ljava/lang/Stri|
000001d0  6e 67 42 75 69 6c 64 65  72 3b 01 00 1c 28 49 29  |ngBuilder;...(I)|
000001e0  4c 6a 61 76 61 2f 6c 61  6e 67 2f 53 74 72 69 6e  |Ljava/lang/Strin|
000001f0  67 42 75 69 6c 64 65 72  3b 01 00 08 74 6f 53 74  |gBuilder;...toSt|
00000200  72 69 6e 67 01 00 14 28  29 4c 6a 61 76 61 2f 6c  |ring...()Ljava/l|
00000210  61 6e 67 2f 53 74 72 69  6e 67 3b 00 20 00 0c 00  |ang/String;. ...|
00000220  0d 00 00 00 00 00 03 00  00 00 0e 00 0f 00 01 00  |................|
00000230  10 00 00 00 1d 00 01 00  01 00 00 00 05 2a b7 00  |.............*..|
00000240  01 b1 00 00 00 01 00 11  00 00 00 06 00 01 00 00  |................|
00000250  00 01 00 0a 00 12 00 13  00 01 00 10 00 00 00 1c  |................|
00000260  00 02 00 01 00 00 00 04  1a 05 68 ac 00 00 00 01  |..........h.....|
00000270  00 11 00 00 00 06 00 01  00 00 00 04 00 09 00 14  |................|
00000280  00 15 00 01 00 10 00 00  00 46 00 03 00 01 00 00  |.........F......|
00000290  00 26 b2 00 02 12 03 b6  00 04 b2 00 02 bb 00 05  |.&..............|
000002a0  59 b7 00 06 12 07 b6 00  08 10 64 b8 00 09 b6 00  |Y.........d.....|
000002b0  0a b6 00 0b b6 00 04 b1  00 00 00 01 00 11 00 00  |................|
000002c0  00 0e 00 03 00 00 00 08  00 08 00 09 00 25 00 0a  |.............%..|
000002d0  00 01 00 16 00 00 00 02  00 17                    |..........|
000002da

Format JAR

Program Java bisa dipackage dalam sebuah file .jar (untuk library, app desktop, applet), war (web), ear. Semuanya sebenarnya adalah file zip biasa yang bisa dibuka dengan program ekstraksi zip (baik via command line ataupun dengan GUI seperti 7zip).

Untuk membuat file JAR, kita perlu membuat manifest.txt, isinya seperti ini

Main-Class: Simple

Buat file jarnya:

jar cvfm simple.jar manifest.txt Simple.class

Sekarang file jar-nya bisa dijalankan

java -jar simple.jar

Kita juga bisa membuat jar tanpa manifest, tapi untuk mengeksekusi jar-nya perlu diberikan nama kelas utamanya seperti ini:

java -cp simple.jar Simple

Melihat Bytecode

Kita bisa melihat bytecode-nya dengan

javap -c -p -s Simple 
Compiled from "Simple.java"
class Simple {
  Simple();
    descriptor: ()V
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 1: 0

  private static int doubleIt(int);
    descriptor: (I)I
    Code:
       0: iload_0
       1: iconst_2
       2: imul
       3: ireturn
    LineNumberTable:
      line 4: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String hello world
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: new           #5                  // class java/lang/StringBuilder
      14: dup
      15: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      18: ldc           #7                  // String double of 100 is:
      20: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: bipush        100
      25: invokestatic  #9                  // Method doubleIt:(I)I
      28: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      31: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      37: return
    LineNumberTable:
      line 8: 0
      line 9: 8
      line 10: 37
}

Kompilasi C# ke Common Intermediate Language (CIL)

Contoh teknologi lain yang menggunakan byte code adalah .NET. Semua bahasa .NET (C#, VB.NET dsb) akan dikompilasi ke Common Intermediate Language (CIL).

Kompilasi

Kompilasi di command line:

mcs Simple.cs
public class Simple
{
   private static int DoubleIt(int a) {
       return 2*a;
   }

   public static void Main()
   {
      System.Console.WriteLine("hello world");
      System.Console.WriteLine("double of 100 is: "  + DoubleIt(100));
   }
}

Melihat CIL

Untuk melihat MSIL menggunakan mono:

monodis Simple.exe

Di Windows, kita juga bisa menggunakan ildasm.exe

.assembly extern mscorlib
{
  .ver 4:0:0:0
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly 'Simple'
{
  .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::'.ctor'() =  (
        01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
        63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01       ) // ceptionThrows.

  .hash algorithm 0x00008004
  .ver  0:0:0:0
}
.module Simple.exe // GUID = {25A1C972-470F-4604-A6EE-176F3D367F7D}


  .class public auto ansi beforefieldinit Simple
    extends [mscorlib]System.Object
  {

    // method line 1
    .method public hidebysig specialname rtspecialname 
           instance default void '.ctor' ()  cil managed 
    {
        // Method begins at RVA 0x2050
    // Code size 7 (0x7)
    .maxstack 8
    IL_0000:  ldarg.0 
    IL_0001:  call instance void object::'.ctor'()
    IL_0006:  ret 
    } // end of method Simple::.ctor

    // method line 2
    .method private static hidebysig 
           default int32 DoubleIt (int32 a)  cil managed 
    {
        // Method begins at RVA 0x2058
    // Code size 4 (0x4)
    .maxstack 8
    IL_0000:  ldc.i4.2 
    IL_0001:  ldarg.0 
    IL_0002:  mul 
    IL_0003:  ret 
    } // end of method Simple::DoubleIt

    // method line 3
    .method public static hidebysig 
           default void Main ()  cil managed 
    {
        // Method begins at RVA 0x205d
    .entrypoint
    // Code size 38 (0x26)
    .maxstack 8
    IL_0000:  ldstr "hello world"
    IL_0005:  call void class [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ldstr "double of 100 is: "
    IL_000f:  ldc.i4.s 0x64
    IL_0011:  call int32 class Simple::DoubleIt(int32)
    IL_0016:  box [mscorlib]System.Int32
    IL_001b:  call string string::Concat(object, object)
    IL_0020:  call void class [mscorlib]System.Console::WriteLine(string)
    IL_0025:  ret 
    } // end of method Simple::Main

  } // end of class Simple

Copyright © 2009-2018 Yohanes Nugroho