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élyra.
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
(Szerintem a Java byte code-t akartad mutatni esetleg, nem a jvm disassembly-t. https://stackoverflow.com/questions/30928786/how-do-i-check-assembly-ou…)
Azért is vetettem el a Java-t mert ott már első blikkre is nagyon macerásnak tűnt megtalálni az assembly-t de kösz a linket, hasznos!
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á.
Persze hogy nincs, mert a glibc egy bloated foshalmaz, nem véletlenül írták meg a MUSL-t helyette.
Használj "-static" kapcsolót (ekkor kimarad a dinamikus linkelésből eredő plt trampoline és a glibc szimbólumok is bekerülnek a dwarf-ba a "-g" hatására), valamint első körben "objdump", az szépen megmondja, felesleges a gdb.
Egyből látszik is, hogy a fő main függvény:
Tehát az _IO_puts nevű függvényt hívja valójában. Ennek a kódja:
Ez már pont úgy néz ki, mint amit a gdb-ben látsz, és a cimkékből az is látszik, hogy itt bizony virtuális metódusokkal történő gányolás esete forog fenn.
Most, hogy már tudjuk, nem is "puts", hanem "_IO_puts" a függvény neve, a glibc forrásában a C kódra rákeresve ezt kapjuk:
És valóban sehol egy syscall, csak még további bloated hivások függvénycímeket tartalmazó táblákból... (megjegyzem, a file descriptorhoz tartozó átirányítás NEM a glibc dolga, hanem a kernelé, ezért itt erre vtable-t használni szimplán faszság).
Dehogynem, csak a glibc egy fos.