kicsi assembly meg gdb bénázás

Ifjoncabb kollégákkal beszélgetve szóba került, hogy még nem láttak assembly kódot, ezért gondoltam mutatok nekik, mégiscsak tudják mi fán terem :)

 

Az volt az ötletem, hogy nézzünk meg egy HelloWorldöt asm-ben meg javaban, hogy lássák tényleg ilyen ilyen obskúrus izék lesznek abból amit ők írkálnak. Na a Javaról hamar letettem, hogy gdb-vel nézegessek egy System.out.println() -t, próbáljuk meg C-ben, de itt is ráballagtam a banánhéjra.

 

Az asm progi:

.global _start

.text

_start:
	mov $1, %rax
	mov $1, %rdi
	mov $message, %rsi
	mov $13, %rdx
	syscall

	mov $60, %rax
	xor %rdi, %rdi
	syscall

.data

message:
	.ascii "Hello World!\n"

 

A C progi:

#include <stdio.h>

int main(void) {
	puts("Hello World!\n");
	return 0;
}

gcc -Wall -g hello.c -o helloc

 

gdb helloc

 

aztán breakpoint a puts-ra és had szaladjon!

...

Reading symbols from helloc...
(gdb) b puts
Breakpoint 1 at 0x1050
(gdb) r
Starting program: /sandbox/asm/helloc 

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) 
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, __GI__IO_puts (str=0x555555556004 "Hello World!\n") at ./libio/ioputs.c:33
33	./libio/ioputs.c: No such file or directory.
(gdb) disassemble
Dump of assembler code for function __GI__IO_puts:
Address range 0x7ffff7c7af40 to 0x7ffff7c7b166:
=> 0x00007ffff7c7af40 <+0>:	endbr64
   0x00007ffff7c7af44 <+4>:	push   %r14
   0x00007ffff7c7af46 <+6>:	push   %r13
   0x00007ffff7c7af48 <+8>:	push   %r12
   0x00007ffff7c7af4a <+10>:	push   %rbp
   0x00007ffff7c7af4b <+11>:	mov    %rdi,%rbp
   0x00007ffff7c7af4e <+14>:	push   %rbx
   0x00007ffff7c7af4f <+15>:	sub    $0x10,%rsp
   0x00007ffff7c7af53 <+19>:	call   0x7ffff7c224c0 <*ABS*+0xa6ff0@plt>
   0x00007ffff7c7af58 <+24>:	mov    0x17aec1(%rip),%r13        # 0x7ffff7df5e20
   0x00007ffff7c7af5f <+31>:	mov    %rax,%rbx
   0x00007ffff7c7af62 <+34>:	mov    0x0(%r13),%r12
   0x00007ffff7c7af66 <+38>:	testl  $0x8000,(%r12)
   0x00007ffff7c7af6e <+46>:	je     0x7ffff7c7b040 <__GI__IO_puts+256>
   0x00007ffff7c7af74 <+52>:	mov    %r12,%rdi
   0x00007ffff7c7af77 <+55>:	mov    0xc0(%rdi),%eax
   0x00007ffff7c7af7d <+61>:	test   %eax,%eax
   0x00007ffff7c7af7f <+63>:	jne    0x7ffff7c7b096 <__GI__IO_puts+342>
   0x00007ffff7c7af85 <+69>:	movl   $0xffffffff,0xc0(%rdi)
   0x00007ffff7c7af8f <+79>:	mov    0xd8(%rdi),%r14
   0x00007ffff7c7af96 <+86>:	lea    0x177d63(%rip),%rdx        # 0x7ffff7df2d00 <_IO_printf_buffer_as_file_jumps>

...

 

Nyilván van a glibc-ben egy csomó más fontos sallang is, de azt furcsállom, hogy egy darab kernel hívás (syscall vagy call 0x80) nincs benne. Persze máshol biztos van, csak így, hogy kb. 0 az átfedés a két kód között, nem túl szemléletes :)

 

Segítsetek kérlek mit, hogy írjak másként, hogy összehasonlítható legyen?

 

Köszi!

Hozzászólások

Nyilván nem fogsz itt syscallt látni, hiszen magát a kernelt a glibc hívja meg, miután platformspecifikus módon implementálja a printf()-et, meg minden más libc függvényt. Ezek az absztrakciós rétegek már csak ilyenek, ez az ára a hordozhatóságnak.

Például a puts belső implementációja is ilyen:

https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/ioputs.c;h=7f719…

Ez ugye láthatólag a belső _IO_sputn-t hívja meg.

Az meg egy makró: https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/libioP.h;h=a83a4…

Ami megint csak egy makró: https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/libioP.h;h=a83a4…

Nem olyan egyszerű ám a libc, mint az ember gondolná.

Az a baj, hogy szép, hogy a musl soványabb, és nem olyan elhízott, mint a glibc, de sajnos van bőven szoftver, ami glibc nélkül nem fog menni. Az is igaz, hogy bloat a glibc, de azért attól is függ, hogy mihez képest nézzük. Ha a Javához, meg a hőn utált Rust-odhoz képest nézzük, azokhoz képest akár soványkának is lehetne nevezni.

“Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

a hőn utált Rust-odhoz képest nézzük
Én nem utálom a Rust-ot. Nem a kedvenc nyelvem, látom a hosszútávú buktatóit, de nem utálom. Amit utálok, az a sok nagypofájú balfasz Rustafári, akik azt hiszik, ők szarták a spanyolviaszt és mindenkinek megpróbálnak beugatni, ha kritikát merészel megfogalmazni az imádott nyelvükkel kapcsolatban. Építő jellegű kritika nélkül sose fog beérni, úgyhogy pont a hypevonatos Rustafárik miatt fog megbukni ez a nyelv.
azokhoz képest akár soványkának is lehetne nevezni.
Inkább a többi libc-hez kell mérni, pl. MUSL, vagy uClibc vagy newlib.

Simán kicserélve nem lett jó.

 

pentike@5CG3464HPJ:~/sandbox/asm$ cat hello.c
#include <stdio.h>

int main(void) {
	write(1,"Hello World!\n",13);
	return 0;
}
pentike@5CG3464HPJ:~/sandbox/asm$ gcc -Wall hello.c -o helloc --static
hello.c: In function ‘main’:
hello.c:4:9: warning: implicit declaration of function ‘write’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
    4 |         write(1,"Hello World!\n",13);
      |         ^~~~~
      |         fwrite

A többiek majd javítanak ha szerintük rosszat mondok, de szerintem ott rontottad el, hogy túl magas szintű eljárásal akartál valamit bemutatni. Ha puts, vagy printf vagy hasonló, akkor az már a "nagy" C-függvénykönyvtár része (man, 3. fejezet) - ilyen feladatra egyértelműen sokkal jobb a rendszerhívásokhoz sokkal közelebbi példákat használó eszköz - ezért is használhatóbb a fenti write-os példa (elvben ugye a write az rendszerhívás -  system call - "rendes" rendszerben a man 2. fejezet.) A man-t amúgy se hátrány ilyen esetben lapozgatni, elvben legalábbis ildomos lenne ilyeneket leírni, ami itt is kiderült, hogy stdio-t vagy unistd-t kell-e inkludálni.

Ajánlom a https://godbolt.org/ oldalt. A bal oldali panelbe beírod a C kódot, jobb oldalon meg kb. valós időben látod az assembly-t, az egymásnak megfelelő részeket azonos színnel kiemelve. Választhatsz egy csomó féle nyelvet és fordítóprogramot.

graal vm-mel lehet binárisra fordítani Java programot. Indulás teljesítmény okokból szokták használni például. Hogy a generált ASM milyen, meg hogy hogy kell megnézni arról fogalmam sincsen sajnos. (Subscribe)