Memprogram Apa Saja

Saya punya hobi memprogram benda apa saja dengan bahasa apa saja. Pertama saya contohkan dulu benda-benda yang saya program, lalu saya akan berusaha menjelaskan kenapa saya punya hobi ini, dan kenapa menyukai hobi ini.

Saya belajar memprogram otodidak waktu kelas 2 SMP dengan komputer Apple II/e dengan bahasa Basic. Waktu itu semuanya masih ngasal karena hanya belajar dari contoh source code. Waktu floppy disk-nya sudah error, saya tetap memprogram tiap hari, iseng membuat sesuatu, walau tidak bisa disimpan dan harus diketik lagi besoknya. Akhirnya berhenti memprogram benda itu setelah mati total.

Di SMU kelas 2 saya baru punya komputer lagi, kali ini IBM PC dengan Windows 95. Di PC tersebut saya memakai Pascal dan Assembly dan juga sudah mulai memprogram memakai Delphi.  Saya mencoba berbagai macam hal di PC (pernah saya tuliskan di sini). Dari sini sudah terlihat kalau saya sangat random, dari mulai memprogram utility sampai game. Apapun yang saat itu menarik buat saya.

Waktu kuliah saya membawa PC saya ke Bandung, tapi kos saya dibobol maling dan PC saya dicuri. Akhirnya saya sering nongkong di lab sampai tutup. Lab waktu tingkat satu dulu sangat memprihatinkan, cuma 486 DX (lebih rendah specnya dari PC pentium saya yang hilang), jadi di situ saya kembali mendalami memprogram mode teks.

Device non PC pertama yang saya program adalah Palm OS. Selanjutnya banyak device sejenis PDA/Smartphone yang saya program. Saya sempat membuat puzzle di PalmOS dan beberapa app kecil lain. Berikutnya adalah Symbian Bible yang sempat dipakai jutaan orang di masa kejayaan Symbian OS (menggunakan C++). Saya juga membuat Bible Plus untuk OS Blackberry (versi BBOS ditulis memakai Java dan versi BB10 di tulis dalam C++ menggunakan Qt).

Saya juga memprogram game console saya. Saya memporting dua aplikasi ke Wii, yaitu WiiApple dan Hatari (keduanya menggunakan C di platform PowerPC). Saya membuat aplikasi dadu di Nintendo DS ketika dadu anak saya hilang (menggunakan C di platform ARM).

11224430_10154009703648488_423812051884387581_n

Sebelum ada Apple Watch, Pebble, dan berbagai jam pintar Android, saya sudah memprogram jam EZ430-Chronos untuk menampilkan OTP (ini memakai C, prosessornya MSP430). Setelah punya jam pintar Android, saya juga membuat versi untuk Android (menggunakan Java).

10689684_10152985303908488_979245536455326491_n

Program yang saya buat kadang khusus cuma untuk mainan anak saya. Misalnya benda ini adalah Arduino dengan Joystick yang akan mengubah arah panah (dan jika ditekan tengahnya akan mengubah menjadi ikon lain).

11700914_10153778798908488_3986090072574211621_n

Ini dipakai di tempat tidur busnya:

11737831_10153778798868488_6085033335527012678_n

Sekedar lampu sirene untuk mobil-mobilan anak saya (menggunakan C di ATTiny13):

10945854_10153355982928488_8889515360750762989_o

Memprogram kereta mainanya (menggunakan C):

1521933_10152323796063488_1552795962_n

Atau mainan berbasis RFID untuk Jonathan yang bisa dilihat di sini (berbasis Python).

8993376562_55ff3846b3

Di mobil, saya memasang display yang terhubung dengan raspberry pi. Karena tidak menemukan media player yang mudah dipakai anak saya, maka saya membuat player sendiri yang dikendalikan dengan Joystick NES (ini menggunakan Python+PyGame).

11741108_10153783327303488_4463593811268542014_o

Kadang saya hanya membuat program kecil yang dipakai sendiri. Misalnya ini untuk belajar ESP8266 (menggunakan bahasa Lua).

10983178_10153384149983488_5592019281940390376_n

Saya juga tidak keberatan memakai berbagai bahasa. Misalnya untuk mainan Puzzle ini, saya memakai Haxe:

IMG_00000145_thumb

Kalkulator bisa diprogram dalam bahasa BASIC

Selain aplikasi yang sifatnya main-main, saya juga melakukan beberapa hal yang agak serius, seperti memporting kernel OS. Misalnya porting FreeBSD ke sebuah SoC ARM.

Di PC, saya memprogram dalam banyak bahasa: assembly, Pascal/Delphi, C, C++, PHP, Python, Lua, Ruby, Java, JavaScript dsb. Sebagian besar yang saya tulis pernah jadi produk atau dipakai di internal perusahaan.

Bagi saya memprogram dengan berbagai teknologi memiliki kepuasan tersendiri. Ketika sebuah program bisa berjalan bagi saya itu sudah merupakan hal yang ajaib. Meskipun saya tahu bagaimana komputer bekerja sampai level bit processing dan memahami teori turing completeness, tetap saja magic.

Beberapa pengalaman terasa lebih magic dibanding sebelumnya. Kali pertama memprogram socket, rasanya takjub dua buah program di dua komputer bisa berinteraksi,  kali pertama memprogram skrip CGI (common gateway interface): wah bisa bikin situs yang dinamis, kali pertama memprogram LED yang bisa berkedip, menggerakkan motor, dan membaca sensor rasanya senang sekali sebuah program bisa berinteraksi langsung dengan dunia fisik.

Pengalaman programming juga membantu saya memahami banyak konsep matematika. Mengenal konsep fungsi di programming membuat saya lebih mudah memahami topik seperti komposisi fungsi. Belajar perkalian matriks ketika membuat game 3D lebih nyangkut di kepala saya daripada ketika belajar di kelas.

Banyak orang menggunakan berbagai alat musik untuk memainkan lagu tertentu. Sebenarnya lagunya sama saja, tapi ada kepuasan tersendiri mendengarkan musik menggunakan alat musik alternatif (misalnya ada yang memainkan Let It Go memakai Game Boy). Sama halnya dengan program: emulator Apple yang berjalan di PC sama saja dengan yang berjalan di Wii, tapi ada kepuasan tersendiri melihat program yang sama bisa berjalan di device yang berbeda. Dalam kasus emulator Apple, Wii bisa terhubung ke TV di ruang tamu dan bisa membangkitkan nostalgia dulu orang-orang memakai komputer Apple dengan monitor TV sebagai displaynya.

Kadang saya menganggap berbagai teknik yang sudah lama tak akan terpakai lagi, dan ternyata saya salah. Dulu waktu ingin membuat aplikasi berjalan di latar belakang di DOS, saya memakai mengintercept interrupt 1ch, supaya dipanggil 18.2 kali per detik. Waktu saya belajar Linux dan Windows dan kemampuan multiprocess dan multithreading, saya pikir: wah ini cara yang mudah dan enak. Tapi ketika berhadapan dengan browser: kembali ke single threading, dan beberapa hal perlu disimulasikan dengan timer. Saat ini support WebWorker sudah memungkinkan multi threading tapi tetap terbatas (contoh: akses DOM tidak bisa dari web worker, jadi processing DOM tetap harus single thread).

Meski sudah saya jelaskan, saya sadar bahwa banyak hobi yang sulit dimengerti oleh orang lain. Selalu ada counter argument kenapa hobi itu “aneh”, atau “nggak berguna”.

Contoh: hobi mendaki gunung. Penjelasannya kenapa seseorang bisa suka: mengenai perjalanannya (it’s all about the journey), mengenai udara segarnya, mengenai pemandangan indahnya. Contoh counter argument-nya: kan capek jalan naik gunung, saya baca naik gunung itu bahaya (udah banyak orang yang meninggal gara-gara tersesat), nanti cari toilet di mana? pemandangannya kan bisa dicari di Google, di taman juga udaranya segar. Saya ambil pake tour aja yang di antar bus ke puncak gunung yang lain, sama aja kan? terus kenapa harus naik gunung yang lain? bukannya pemandangannya mirip-mirip aja?

Mungkin bagi orang awam berbagai kamera sama saja, lensa yang mahal versus mahal banget tidak terlihat bedanya. Dan masih banyak lagi hal-hal lain yang cuma bisa diapresiasi pemilik hobi.

Hanya karena saya suka memprogram berbagai device tidak membuat saya jadi jago/pintar memprogram, hanya membuka pikiran saya lebih luas. Sama seperti orang yang hobi naik gunung tidak membuat orang tersebut jadi ahli, walaupun mungkin dia lebih sehat dari aktivitas mendaki dibanding yang diam saja di rumah. Dari hobi saya, saya tahu sedikit lebih banyak dari mereka yang tidak punya hobi programming.

Tulisan ini cuma sekedar sharing hobi aneh saya karena saya masih jarang ketemu orang yang senang memprogram apa saja. Siapa tahu bisa menarik orang lain untuk hobi yang sama.

Memahami Static dan Shared Library di Linux

Saya masih sering melihat programmer C dan juga administrator yang bingung dengan konsep shared library. Shared library adalah file berisi kode yang bisa diload saat program dieksekusi (runtime). Karena diload pada runtime, maka sebuah shared library bisa digunakan oleh lebih dari satu program.

Penjelasan mengenai static dan shared library biasanya membingungkan, jadi di posting ini saya akan menjelaskan dengan banyak contoh. Sebenarnya hampir semua contoh di tulisan ini berlaku juga untuk lingkungan POSIX lain selain Linux, tapi saya hanya mencoba kode ini di Linux 64 bit dengan compiler gcc. Di balik layar, program gcc sebenarnya akan memanggil berbagai program lain (preprocessor, assembler, linker) tergantung pada parameter yang kita berikan tapi agar penjelasannya sederhana, saya akan memakai gcc saja dan tidak akan menjelaskan apa yang terjadi di balik layar.

Kode monolitik

Kita mulai dari kode yang sangat sederhana seperti ini:

/*file: main.c */
#include <stdio.h>

double operation(double a, double b)
{
        printf("Plus operation\n");
        return a+b;
}

int main(int argc, char *argv[])
{
        double a = 5;
        double b = 3;
        printf("Result of operation (%.2f, %.2f) is: %.2f\n", a, b, operation(a, b));
        return 0;
}

Karena semua sudah ada di satu file, maka ini bisa dikompilasi dan jalankan langsung.

gcc main.c -o main

Memecah source code

Di sini ada satu fungsi bernama operation yang hanya melakukan operasi sangat sederhana. Anggap saja fungsi ini rumit dan penting dan ingin kita pisahan agar bisa dipakai oleh orang lain. Sekarang operation saya pindahkan ke operation.c

/*file: operation.c */
#include <stdio.h>

double operation(double a, double b)
{
        printf("Plus operation\n");
        return a+b;
}

Dan kode main menjadi:

/*file: main1.c */
#include <stdio.h>

int main(int argc, char *argv[])
{
        int a = 5;
        int b = 3;
        printf("Result of operation (%d, %d) is: %d\n", a, b, operation(a, b));
        return 0;
}

Jika kita coba compile main.c saja, seperti ini:

gcc main.c -o main

maka akan ada warning DAN error. Isi errornya adalah:

/tmp/cc08zl1h.o: In function `main':
main2.c:(.text+0x45): undefined reference to `operation'
collect2: error: ld returned 1 exit status

Ini karena compiler tidak bisa menemukan implementasi dari fungsi operation. Kita perlu memberikan file operation.c

gcc main1.c operation.c -o main

Sekarang kompilasi berhasil, tapi tetap ada peringatan:

implicit declaration of function ‘operation’

Compiler C tidak tahu menahu mengenai fungsi bernama operation. Secara default (karena alasan sejarah), compiler akan menganggap fungsi tersebut mengembalikan sebuah int

Jika kita coba jalankan:

Plus operation
Result of operation (5.00, 3.00) is: 0.00

Hasilnya 0.00 karena fungsi operation dianggap mengembalikan int. Ini bisa diperbaiki dengan menambahkan deklarasi fungsi sebelum main:

/*file: main2.c */
#include <stdio.h>

/* INI YANG DITAMBAHKAN */
double operation(double a, double b);

int main(int argc, char *argv[])
{
        int a = 5;
        int b = 3;
        printf("Result of operation (%d, %d) is: %d\n", a, b, operation(a, b));
        return 0;
}

Sekarang kita coba lain:

$ gcc main2.c operation.c -o main

Dan berhasil:

Plus operation
Result of operation (5.00, 3.00) is: 8.00

Object code

Tapi sekarang kita tidak ingin orang mengetahui source code operation.c, kita bisa menjadikan operation.c menjadi object code:

$ gcc -c operation.c

Hasilnya adalah file operation.o. Ini bisa dikirimkan ke orang lain yang memiliki main.c dan kompilasi bisa dilakukan dengan (perhatikan: operation.c diganti menjadi .o)

$ gcc main2.c operation.o -o main

Tapi ada satu masalah di sini: penerima operation.o tidak tahu fungsi apa di dalam operation.o dan apa parameternya. Sebenarnya nama fungsinya saja bisa dilihat dengan nm atau objdump tapi apa parameternya tidak bisa. Contoh dengan nm:

$ nm operation.o
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T operation
                 U puts

File Header

Untuk memudahkan, kita perlu membuat file header: operation.h, isinya bisa seperti ini saja:

double operation(double a, double b);

Dan di file yang memakai fungsi tadi, bisa menggunakan include:

/*file: main3.c */
#include <stdio.h>
/* INI YANG DITAMBAHKAN */
#include "operation.h"

/*ini tidak lagi diperlukan, karena sudah diinclude dari operation.h*/
/*double operation(double a, double b);*/

int main(int argc, char *argv[])
{
        int a = 5;
        int b = 3;
        printf("Result of operation (%d, %d) is: %d\n", a, b, operation(a, b));
        return 0;
}

Sekarang kita sudah bisa mengirimkan file operation.h dan operation.o untuk dipakai oleh seseorang. Orang tersebut tidak tahu implementasinya (kecuali dengan dekompilasi), dan bisa memakai fungsinya dengan mudah. Perlu dicatat bahwa ada beberapa standar file object, secara umum: file dari compiler yang sama akan kompatibel, tapi tidak dijamin kompatibel dengan kode dari compiler yang berbeda.

Biasanya file header tidak hanya berisi deklarasi fungsi, tapi juga deklarasi struktur, misalnya seperti ini:

struct point {
int x, y;
};

void init_point(struct point *p);

Jika file ini di-include sekali saja, maka tidak ada masalah. Tapi jika tidak sengaja diinclude dua kali, maka akan ada error:

 error: redefinition of ‘struct point’

Bagaimana mungkin menginclude “tidak sengaja” dua kali (atau lebih?). Contohnya ada file circle.h menginclude file point.h, dan file square.h juga menginclude point.h. Jika program utama menginclude circle.h dan square.h maka point.h akan diinclude dua kali. Masalah ini sering muncul sehingga ada konvensi membuat header seperti ini:

#ifndef POINT_H
#define POINT_H
struct point {
int x, y;
};

void init_point(struct point *p);
#endif

Ketika file diinclude kali pertama, semuanya akan normal dan POINT_H akan terdefinisi. Jika diinclude lagi, maka tidak akan terjadi apa-apa karen POINT_H sudah terdefinisi.

Sekarang ada masalah baru. Andaikan kita ubah operation.c sehingga menggunakan int, tapi lupa mengubah header operation.h

/*file: operation.c */
#include <stdio.h>

int operation(int a, int b)
{
        printf("Plus operation\n");
        return a+b;
}

Maka semuanya akan berhasil dicompile seperti semula, tapi hasilnya tidak seperti yang diharapkan. Untuk mengatasi ini, kita sebaiknya menginclude header di implementasi, seperti ini:

/*file: operation.c */
#include <stdio.h>
/* BARIS INI DITAMBAHKAN */
#include "operation.h"

int operation(int a, int b)
{
        printf("Plus operation\n");
        return a+b;
}

Sekarang jika deklarasi dan implementasi tidak konsisten akan mucul error:

operation.c:5:5: error: conflicting types for ‘operation’
 int operation(int a, int b)
     ^~~~~~~~~
In file included from operation.c:3:0:
operation.h:4:8: note: previous declaration of ‘operation’ was here
 double operation(double a, double b);

Static Library

Sekarang header kita sudah bagus. Tapi ada masalah lain: jika hanya ada satu file saja, maka satu file objek sudah cukup. Tapi jika ada banyak file objek, ini akan merepotkan. Contohnya saja, jika kita memiliki kode untuk menangani bentuk, dan menggunakan banyak objek seperti: circle.o, square.o, polygon.o, dsb, maka kompilasinya akan cukup repot (perintahnya menjadi panjang). Berbagai file objek bisa disusun jadi satu library statik untuk memudahkan kompilasi.

Kita bisa menyusun satu file saja:

ar rcs libop.a operation.o

Atau banyak file

ar rcs libop.a operation.o operation2.o

Progam “ar” akan menciptakan arsip library, dengan flag “c” untuk membuat library (create) jika belum ada, “r” untuk menggantikan (replace) file operation.o jika sudah ada di arsip dan “s” untuk membuat indeks. Kita bisa melihat isi arsip .a dengan parameter “t” (test):

ar t libop.a

Sudah menjadi konvensi untuk menamai suatau library statik dengan prefiks lib dan dengan suffiks a. Kita bisa memakai library ini ketika mengkompilasi dengan menggunakan opsi l diikuti nama library tanpa prefix lib (cukup:op saja)di gcc:

gcc main3.c -lop

Tapi ini akan error:

/usr/bin/ld: cannot find -lop
collect2: error: ld returned 1 exit status

Karena defaultnya gcc hanya mencari di path library. Path standar ini bisa dilihat dengan:

$ gcc -v -Wl,verbose 2>&1|grep LIBRARY

Di komputer saya outputnya seperti ini:

LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/

Intinya: kita bisa memindahkan file libop.a ke salah satu direktori tersebut, atau cukup beritahu path tempat library kita berada. Karena berada di direktori saat ini, kita bisa memakai titik tunggal (.):

gcc main3.c -L. -lop -o main

Sekarang program berhasil dilink dengan library, dan berjalan seperti biasa. Sebagai catatan, sebenarnya gcc juga mengijinkan kita langsung menyebut nama file librarynya, walau biasanya ini jarang dilakukan

gcc main3.c libop.a -o main

Andaikan kita salah melink ke library yang tidak mengandung implementasi fungsi operation, maka akan muncul error:

main.c:(.text+0x40): undefined reference to `operation'
collect2: error: ld returned 1 exit status

Ketika kita menggunakan static library, maka kode dari library tersebut di “copy paste” ke file executable. Untuk menjalankan program kita, kita tidak butuh lagi libop.a karena kodenya sudah ada di executable. Jika ada program lain yang memakai library yang sama, program itu mengandung kode yang sama (duplikat).

Andaikan kita mengupdate operation.c dengan versi yang lebih baik (misalnya ada perbaikan bug, atau ada optimasi baru) maka kita harus mengcompile ulang program kita agar mendapatkan perbaikan tersebut.

Shared Library

Dalam kasus tertentu kita ingin agar hanya ada satu kode saja yang dipakai bersama. Misalnya sebuah XML parser dipakai oleh banyak program, jika ada update pada parser XML kita tidak ingin mengkompilasi ulang seluruh program yang memakai library tersebut. Dalam kasus ini kita sebaiknya menggunakan shared library. Satu library yang bisa dipakai banyak program.

Untuk mengcompile operation.c menjadi shared library bisa dilakukan dengan:

gcc -fPIC -shared operation.c -o libop.so

Cara kompilasi juga sama dengan static library (jika ada libop.a dan libop.so, maka yang .so diprioritaskan):

gcc main3.c -L. -lop -o main

atau:

gcc main3.c libop.so -o main

Tapi jika kita coba jalankan:

$ main
./main: error while loading shared libraries: libop.so: cannot open shared object file: No such file or directory

Kode dari libop.so tidak disalin ke executable dan masih ada di libop.so, jika kita ingin menjalankan kita harus melakukan salah satu dari ini: menyalin libop.so ke path library sistem, menambahkan path system baru (bisa mengedit /etc/ld.so.conf) atau memberikan pathnya dengan LD_LIBRARY_PATH.

Untuk melihat pencarian yang dilakukan oleh sistem:

$ ldconfig -v 

Jika kita mengubah file /etc/ld.so.conf, kita perlu menjalankan ldconfig sebagai root untuk mengupdate cache.

Cara termudah tanpa akses root adalah menambahkan ke environment LD_LIBRARY_PATH (titik adalah direktori saat ini, bisa saja diganti path lain, misalnya /home/yohanes/mylibs).

$ export LD_LIBRARY_PATH=. 
$ ./main
Plus operation
Result of operation (5.00, 3.00) is: 8.00

Cara di atas akan membuat semua perintah berikutnya menggunakan path library yang baru. Jika kita hanya sekedar ingin mengubah path untuk satu perintah saja, maka cara ini lebih baik:

$ LD_LIBRARY_PATH=. ./main
Plus operation
Result of operation (5.00, 3.00) is: 8.00

Fleksibilitas shared library

Sekarang kita lihat fleksibilitas shared library dengan membuat shared library baru dengan nama sama. Saya membuat file baru: operation-mult.c yang mengalikan operand.

#include <stdio.h>

double operation(double a, double b)
{
	printf("Multiply operation\n");
	return a*b;
}

Agar lebih jelas, saya masukkan file ini dalam direktori “op-mult”. Lalu saya compile seperti ini:

gcc -fPIC -shared op-mult/operation-mult.c -o op-mult/libop.so

Sekarang jika saya jalankan program sebelumnya, tapi dengan path libop.so yang berbeda:

$ LD_LIBRARY_PATH=./op-mult ./main
Multiply operation
Result of operation (5.00, 3.00) is: 15.00

Output program berubah: dari penjumlahan menjadi perkalian. Jika kita memiliki lebih dari satu library dengan nama yang sama di path yang berbeda, maka library yang ditemukan pertama akan diload

$ LD_LIBRARY_PATH=./op-mult:. ./main
Multiply operation
Result of operation (5.00, 3.00) is: 15.00
[/code]

Bagaimana jika kita punya file yang kebetulan namanya sama, tapi tidak memiliki fungsi yang kita butuhkan? Hasilnya akan error:

./main: symbol lookup error: ./main: undefined symbol: operation

Override Fungsi dengan LD_PRELOAD

Ada trik menarik yang bisa dilakukan dengan menggunakan shared library, yaitu mengganti implementasi fungsi lain dengan fungsi milik kita sendiri. Trik ini bisa dipakai untuk runtime patching.

Dengan menggunakan program ltrace kita bisa melihat fungsi library apa yang dipanggil oleh sebuah program. Untuk mudahnya, kita akan memakai program paling pertama, yang tidak memakai library, versi monolitik:

ltrace ./main
puts("Plus operation"Plus operation
)                                                = 15
printf("Result of operation (%d, %d) is:"..., 5, 3, 8Result of operation (5, 3) is: 8
)                = 33
+++ exited (status 0) +++

Ada 2 call yang dibuat oleh program tersebut: puts dan printf. Sebenarnya di dalam program kita memakai printf saja, tapi compiler mengoptimasi printf tanpa parameter tambahan menjadi puts saja. Jika kita meminta compiler untuk tidak menghapus file assembly dengan opsi -S

$ gcc -S main.c -o main

Maka kita bisa melihat bahwa dalam operation memang digunakan puts sedangkan dalam main digunakan printf:

Cara lain melihat ini adalah dengan objdump -d nama_executable untuk melihat kodenya.

Untuk mudahnya sekarang kita ingin mengganti puts agar melakukan hal lain: mencetak string “EXTRA” sebelum mencetak string yang seharusnya.

/*file: myputs.c*/

#define _GNU_SOURCE  
#include <dlfcn.h>

int (*orig_puts)(const char *s);

int puts(const char *s)
{
        if (orig_puts == 0) {
                orig_puts = dlsym(RTLD_NEXT, "puts");
        }
        orig_puts("EXTRA: ");
        return orig_puts(s);
}

Lalu compile filenya sebagai shared library, tapi sertakan juga libdl (untuk fungsi dlsym).

$ LD_PRELOAD=./libmyputs.so ./main-standalone 
EXTRA: 
Plus operation
Result of operation (5, 3) is: 8

Dalam kode pengganti milik kita, pertama yang dilakukan adalah mencari alamat fungsi “puts” yang asli, lalu meletakkannya di variabel bernama orig_puts (tiipenya adalah sebuah function pointer). Setelah itu kita bisa memanggil fungsi aslinya. Tentunya kita tidak harus memanggil fungsi aslinya jika memang ingin mengganti seluruhnya.

Beberapa kegunaan LD_PRELOAD ini misalnya: untuk membetulkan bug tanpa mengubah program. Kadang ini juga bisa digunakan untuk testing, jika sebuah program memakai random, lalu crash hanya jika nilai tertentu dihasilkan oleh random, maka kita bisa menggunakan LD_PRELOAD untuk mengganti fungsi random agar nilai kembaliannya sebuah nilai yang tetap.

LD_PRELOAD Ini juga bisa digunakan untuk cracking program, misalnya jika program hanya bisa berjalan hanya sebelum expiration date, maka kita bisa membuat fungsi time() yang selalu mengembalikan tanggal tertentu.

Penutup

Pemahaman mengenai library bisa membantu menyelesaikan banyak masalah dan tidak perlu coba-coba yang memakan banyak waktu. Semoga posting ini cukup menjelaskan seluk beluk library di Linux.

Mengapa memakai bahasa pemrograman tertentu?

Mengapa seseorang memilih suatu bahasa tertentu untuk menyelesaikan suatu masalah atau membuat aplikasi tertentu? Ternyata jawabannya bisa banyak.  Kebanyakan pilihan intinya adalah keterpaksaan dan terakhir baru faktor kenyamanan.

Keterpaksaan pertama dari non teknis, misalnya dari permintaan atasan atau permintaan client. Hal ini sering kali tidak bisa ditawar lagi, terutama jika sudah melibatkan kontrak legal. Paksaan ini sering menghasilkan kode yang aneh atau tidak menggunakan fitur yang tepat dari sebuah bahasa, karena programmer dipaksa menggunakan bahasa lain, dan karena harus buru-buru mereka akan menggunakan gaya bahasa X di bahasa Y.

Sebuah teknologi tertentu kadang hanya bisa diprogram dengan satu bahasa. Misalnya dulu ponsel cuma bisa diprogram dengan Java (J2ME) jadi ya terpaksa harus memakai bahasa Java. Microcontroller tertentu perlu memakai assembly karena ukuran ROMnya sangat kecil dan tidak ada compiler C yang bisa menghasilkan kode sekecil itu.

Sekarang ini jumlah memori dan kecepatan berbagai device sudah sangat tinggi, sehingga memungkinkan interpreter berjalan di platform apa saja. Ini memberi kebebasan memilih bahasa. Contohnya: dulu microcontroller CPU dan RAMnya sangat kecil sehingga tidak cukup untuk menjalankan interpreter Python. Sekarang sudah banyak microcontroller yang bisa menjalankan MicroPython.

Bahasa tertentu dipilih karena masalah kinerja (performance). Meskipun Python bisa berjalan di microcontroller, tapi kecepatannya jauh lebih lambat dibandingkan C/C++ (karena interpreted). Faktor kecepatan ini sangat terasa untuk keperluan tertentu, misalnya untuk membuat animasi lampu yang jumlahnya banyak. Di dalam konteks lain juga sama: bahasa tertentu dipilih karena masalah performance.

Berbagai bahasa memiliki tingkat strictness yang berbeda, dan mulai masalah sintaks sampai tipe data. Sebagian bahasa memungkinkan kecerobohan yang sulit dilakukan di bahasa lain. Contoh sederhana, dalam JavaScript kita bisa membuat kode seperti ini dan akan berjalan, walau hasilnya mungkin tidak seperti yang diharapkan.

Tipe a adalah Number dan b adalah String, tapi keduanya bisa dioperasikan

Pemula Ruby yang beralih dari bahasa tertentu (misalnya C) mungkin akan bingung dengan sifat Ruby di mana 0 dianggap true. Alasannya: 0 adalah object yang valid, dan nilainya dianggap true.

Untuk bahasa yang sangat dinamik, dibutuhkan banyak test case untuk banyak hal sederhana, sedangkan di bahasa yang tipenya dicek oleh compiler, jumlah test bisa dikurangi. Ada bahasa yang lebih strict lagi, misalnya bahasa ADA yang sempat jadi bahasa wajib oleh Department of Defense Amerika. Secara umum: bahasa tertentu mengurangi kesalahan programmer (baik dari syntax, dari compiler), sehingga bahasa tersebut dipilih untuk tujuan tertentu.

Beberapa masalah mudah diselesaikan menggunakan library yang sudah ada. Ketersediaan library ini menjadi alasan kenapa memilih sebuah bahasa. Misalnya sudah ada library OpenCV (Open computer vision) untuk memproses video secara realtime (misalnya face recognition). OpenCV ditulis dalam C++ dan  ada banyak binding sehingga fungsi-fungsi C++ ini bisa diakses dari bahasa lain (misalnya Python, Java, C#, NodeJS) tapi tidak semua binding ini sempurna, dan sebagian ketinggalan versinya. Misalnya saat tulisan ini dibuat, binding OpenCV untuk JavaScript/NodeJS versi 3 belum ada.

Jika tidak ada batasan keterpaksaan, maka alasan berikutnya adalah kenyamanan. Beberapa aplikasi bisa ditulis dalam bahasa apapun karena tidak tidak keterpaksaan tertentu. Dalam kasus seperti ini, orang bisa memilih bahasa sesuai dengan familiaritas dengan bahasa tersebut. Sebagian programmer yang menguasai banyak bahasa akan memilih bahasa yang ternyaman (dan kalau bisa yang terbaik) untuk menyelesaikan suatu masalah.

Jangan heran kalau dalam sebagian masalah, C bisa lebih nyaman dari Java atau bahasa lain.  Contoh kecil: sering kali dalam memprogram hardware kita membutuhkan array of bytes (misalnya Protocol Data Unit pada NFC). Inisialisasi array of byte di Java jika nilainya kurang dari 127 bisa dilakukan seperit ini:

byte b [] = new byte[] {1, 127};

Tapi jika nilainya lebih dari 127 tiba-tiba kodenya tidak bisa dicompile:

byte b [] = new byte[] {1, 128};

Dengan error:

incompatible types: possible lossy conversion from int to byte

Cara yang benar adalah dengan menggunakan casting ke byte:

byte b [] = new byte[] {1, (byte)128};

Sedangkan di C, dengan tipe uint8_t (dari header stdint.h) kita tidak perlu memperdulikan hal kecil seperti itu. C juga mendukung bit fields yang bisa membuat manipulasi bit tidak diperlukan (karena dilakukan oleh compiler).

Di sisi lain: membuat aplikasi web lebih sulit dilakukan dengan C. Bereksperimen dengan bilangan integer yang besar (ratusan digit, misalnya untuk operasi RSA) jauh lebih mudah dilakukan di Python dibandingkan Javascript (big integer sudah built in di Python).

Perlu dicatat bahwa bahasa pemrograman yang populer juga masih terus berkembang. Dulu untuk melakukan iterasi list di Java butuh kode yang cukup panjang, tapi kemudian syntax for ditambahkan yang memudahkan ini. Ini hanyalah syntactic sugar alias pemanis saja, di dalamnya implementasinya masih sama. Sebagian bahasa memiliki sintaks yang sudah manis dari sejak dirancang sehingga kadang seseorang lebih memilih bahasa lain.

Kadang tool yang tersedia untuk sebuah bahasa juga menjadi faktor penting dalam memilih sebuah bahasa. Tools ini bisa berupa: compiler, debugger, IDE, dll. Compiler yang lambat bisa membuat frustrasi, jika debugger tidak tersedia akan membuat development lebih lama, IDE yang jelek bisa memperlama coding (apalagi jika sering error/hang) tapi IDE yang bagus bisa mempercepat coding.

Semoga sekarang cukup jelas kenapa ada banyak bahasa, kenapa seseorang perlu mempelajari banyak bahasa, dan kenapa bahasa tertentu dipilih untuk menyelesaikan masalah tertentu.