Memakai System Call Linux Di Assembly

Untuk memanggil system call Linux, programmer assembly harus mengetahui nomor definisi system call yang disebut system call numbers.  Informasi ini dapat diperoleh dengan membaca file /usr/include/asm/unistd_32.h.  Berikut ini adalah contoh isi file tersebut:

#ifndef _ASM_X86_UNISTD_32_H
\#define _ASM_X86_UNISTD_32_H

/*
* This file contains the system call numbers.
*/

\#define __NR_restart_syscall      0
\#define __NR_exit          1
\#define __NR_fork          2
\#define __NR_read          3
\#define __NR_write          4
\#define __NR_open          5
\#define __NR_close          6
\#define __NR_waitpid          7
\#define __NR_creat          8
\#define __NR_link          9
\#define __NR_unlink         10
\#define __NR_execve         11
...
\#define __NR_inotify_init1    332
\#define __NR_preadv        333
\#define __NR_pwritev        334
\#define __NR_rt_tgsigqueueinfo    335
\#define __NR_perf_event_open    336

Isi file di atas menunjukkan bahwa pada sistem operasi Linux tersebut, terdapat 337 system call yang dapat dipanggil oleh programmer assembly, mulai dari 0 hingga 336. Setelah menemukan system call number, langkah berikutnya adalah menentukan apa saja yang dibutuhkan untuk memanggil system call tersebut.  Salah satu cara yang dapat dilakukan adalah dengan memanggil perintah man.  Walaupun ditujukan bagi programmer C, informasi yang ditampilkan oleh man cukup berguna bagi programmer assembly yang ingin memanggil system call tersebut.  Sebagai contoh, bila programmer ingin melihat informasi untuk system call ber-nomor 4 (didefinisikan sebagai __NR_write), maka ia dapat memberikan perintah:

$ man 2 write
NAME
write - write to a file descriptor

SYNOPSIS
\#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

...

Untuk mengerjakan system call di Linux, programmer assembly menggunakan INT dengan nomor interrupt 0x80.  Sebelum memberikan INT 0x80, nomor system call (sesuai dengan yang ada di file unistd_32.h) harus diletakkan terlebih dahulu di register EAX, sehingga kernel Linux dapat mengetahui system call apa yang akan dikerjakan.  Selain itu, bila ada, nilai parameter harus diberikan pada register tertentu dengan urutan seperti berikut ini:

  • Nilai parameter pertama di-isi pada register EBX
  • Nilai parameter kedua di-isi pada register ECX
  • Nilai parameter ketiga di-isi pada register EDX
  • Nilai parameter keempat di-isi pada register ESI
  • Nilai parameter kelima di-isi pada register EDI
  • Bila jumlah parameter lebih dari lima, EBX mengandung pointer ke lokasi memori yang berisi input parameter secara berurutan.

Pada system call write, informasi yang diberikan oleh man pages menunjukkan bahwa dibutuhkan tiga parameter, yaitu fd, *buf, dan count.  Dengan demikian:

  • Register EBX mengandung file descriptor (fd)
  • Register ECX mengandung pointer ke string yang akan ditampilkan (*buf)
  • Register EDX mengandung informasi jumlah karakter di string tersebut (count)

Nilai kembalian dari sebuah system call akan ditampung di register EAX.

Contoh kode program assembly secara lengkapnya akan menjadi:

$ vi test.s
.section .data
tulisan:
.ascii "Ini program pertama saya.\n"

.section .text
.globl _start
_start:
movl $4, %eax           # System call nomor 4 (write)
movl $1, %ebx           # Parameter 1: file descriptor = 1 (stdout)
movl $tulisan, %ecx     # Parameter 2: string yang dicetak
movl $26, %edx          # Parameter 3: jumlah karakter (26 huruf)
int $0x80               # Memanggil kernel Linux

movl $1, %eax           # System call nomor 1 (exit)
movl $0, %ebx           # Parameter 1: status code
int $0x80               # Memanggil kernel Linux

$ as -o test.o test.s
$ ld -o test test.o
$ ./test
Ini program pertama saya.
Iklan

I/O Programming: Mengatur Lampu LED di Keyboard

Belakangan ini saya kerap bertemu dengan seorang dosen yang mengajar mata kuliah sistem operasi, dan kami sering terlibat dalam diskusi menarik.  Mengajarkan materi sistem operasi yang berorientasi praktek membutuhkan tantangan lebih besar.  Hal ini tidaklah berlebihan, karena kebanyakan teori sistem operasi tidak cukup berguna untuk dipakai dalam membuat sistem operasi.  Misalnya, dalam mata kuliah sistem operasi, hampir tidak pernah membahas tentang keyboard controller.  Tetapi, pada kenyataannya, pemahaman akan cara kerja keyboard controller wajib dibutuhkan dalam pembuatan sebuah sistem operasi.

Untuk membaca dan menulis informasi ke keyboard controller, seseorang perlu membaca dan menulis port 0x60 dan 0x64.  Dengan membaca port 0x64, programmer dapat mengetahui status keyboard controller.  Dengan menulis pada port 0x60, seseorang dapat mengirimkan perintah pada keyboard controller.  Misalnya, dengan mengirim nilai 0xED pada port 0x60, programmer dapat mengatur indikator LED apa saja yang nyala yang diwakili dengan nilai berikutnya yang dikirim pada port 0x60.

Pertanyaan pertama yang harus dijawab adalah platform apa yang harus dipergunakan oleh mahasiswa?  Bahasa pemograman yang dipergunakan dapat berupa assembly dan C, dimana bahasa C adalah pilihan yang lebih masuk akal.    Bila menggunakan platform Windows, maka mahasiswa akan memakai Visual C++, sebuah tools standar untuk pemograman low-level di Windows.  Sayangnya, penggunaan fungsi _inp() dan _outp() di Visual C++ dibatasi hanya pada pemograman driver saja.   Hal ini dilakukan dengan alasan keamanan, sehingga tidak sembarangan aplikasi boleh melakukan akses I/O yang notabene dapat berbahaya sekali.  Pilihan lain adalah menggunakan platform lain, misalnya menggunakan sistem operasi Linux.  Pada Linux, tersedia fungsi ioperm() yang memungkinkan program biasa melakukan operasi I/O.  Syaratnya adalah program tersebut dijalankan dengan hak akses super user.

Berikut adalah sebuah contoh program C di Linux yang membuat LED keyboard (LED indikator num lock, caps lock, dan scroll lock) menyala berurutan:

#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>

void kirimCommand(char command) {
  char c = 0;
  while (1) {
    c = inb(0x64);
    if ((c&2)==0) break;
  }
  outb(command, 0x60);
}

int main() {
  if (ioperm(0x60,5,1)) {
    printf("Program tidak memiliki hak akses\n");
    return -1;
  }
  printf("Perhatikan indikator LED di keyboard!\n");
  int i=0;
  char statusLED = 1;
  while (i<100) {
    kirimCommand(0xED);
    kirimCommand(statusLED);
    if (statusLED==4) statusLED=1; else statusLED<<=1;
    i++;
    sleep(1);
  }
}

Pada fungsi kirimCommand(char command) di atas, setiap kali sebelum mengirim command ke port 0x60, program akan memeriksa apakah buffer input sudah kosong.  Hal ini dilakukan dengan membaca status keyboard controller di port 0x64, dan memeriksa isi dari bit 2.

Variabel statusLED berisi pola LED yang akan dinyalakan nantinya.  Hanya tiga bit yang dipakai, yaitu bit 0 untuk LED Scroll Lock, bit 1 untuk LED Caps Lock, dan bit 2 untuk LED Num Lock.

Setelah itu, program dapat di-compile dengan gcc dan dijalankan, dengan memberikan perintah seperti:

$ gcc -o keyboard keyboard.c
$ sudo ./keyboard
[sudo] password for user:
Perhatikan indikator LED di keyboard!

New Job: First Day

Hari Selasa, 14 Juli yang lalu, aku memulai hari pertamaku di pekerjaan baru.   Aku bekerja sebagai programmer C di sebuah perusahaan yang bergerak di bidang telekomunikasi.  Peralihan dari programmer Java menjadi programmer C membuatku butuh waktu untuk beradaptasi.  Tapi aku sangat senang karena aku menemukan banyak hal-hal baru disini.

Salah satu contohnya adalah melakukan development dengan ssh ke server untuk development.  Karena saat ini aku tidak mendapatkan remote GUI, aku hanya mendapatkan shell UNIX saja.  Itu artinya, aku tidak dapat melakukan coding C memakai Eclipse atau IDE berbasis GUI lainnya.  Aku terpaksa harus coding dengan memakai Vim (Vi Improved).  Dulunya, aku selalu menganggap remeh editor Vim dan menolak segala alasan untuk memakai editor ini.  Tapi sekarang, saat terpaksa harus memakainya, aku baru merasakan kecanggihannya.  Vim bukan hanya sekedar editor sederhana, ia mendukung split window, copy (istilahnya yanking) dan paste, syntax highlighting, keyword completion, membuka source dari sebuah function (harus men-build tag dulu sebelumnya), dan fitur lainnya.  O ya, Vim juga bisa memanggil make secara langsung, kemudian memproses compilation error (seperti memindahkan langsung kursor ke baris yang error). Vim ternyata memiliki fitur-fitur yang seharusnya dimiliki oleh sebuah IDE.

Bicara soal Make, aku juga mempelajari syntax makefile.  Fungsi Make mirip seperti Apache Ant, untuk mempermudah compiling & deployment kode program.  Hanya saja, perintah di sebuah makefile tergantung pada shell yang dipakai.   Jika aku membuat sebuah makefile yang hanya berisi perintah untuk shell di Unix, maka makefile tersebut tidak berguna di Windows.

Soal programming, aku aku harus mempelajari banyak hal yang berkaitan dengan system programming.  Selama dua hari pertama, aku mengerjakan latihan dengan membuat program C yang multi-thread (dengan bantuan pthread.h).  Program tersebut akan memiliki thread yang berfungsi sebagai listener (membaca pesan dari socket) dan sebuah thread lagi sebagai consumer.  Untuk melakukan operasi TCP/IP, aku memanfaatkan fitur socket bawaan Unix (sys/socket.h).  Operasi socket di Unix tidak berbeda jauh dari Java, karena bisa dibilang Java memang meniru operasi “socket” di Unix.

Tiga hari pertamaku cukup menyenangkan disini; aku menghabiskan banyak waktu untuk belajar… semoga akan terus begitu 🙂