Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Linux Assembly How To: “Hello, World” (tldp.org)
129 points by mindcrime on Aug 12, 2015 | hide | past | favorite | 35 comments


> Linux is 32-bit, runs in protected mode, has flat memory model, and uses the ELF format for binaries.

Well, that's a touch out-of-date :)

All the same, I do find it fun to see the set of articles recently trying to show modern programmers that assembly isn't as terrifying as people make it out to be. My own opinion is that, though asm tends to be a bit more verbose and tedious than other higher-level languages, it looks like it's also dramatically simpler in a lot of ways.

For examle, the Heavything[1] library from 2ton suggests to me that asm can still be used for high-performance applications today.

Good find and a fun read!

[1] https://2ton.com.au/HeavyThing/


Thanks for that, and a tip of my hat to the HN community. Love this place.

Having written the aforementioned, I thought maybe y'all would be interested in a very very lightweight version of same done w/ my assembler of choice (only because it reminds me of Turbo Assembler from way back in the day).

  format ELF64 executable
  _start:
  	mov	eax, 1		; syscall # = write
  	mov	edi, 1		; man 2 write arg1 == our fd, stdout
  	mov	esi, .msg	; "" arg2 == const void *buf
  	mov	edx, .msglen	; "" arg3 == size_t count
  	syscall
  	mov	eax, 60		; syscall # = exit
  	xor	edi, edi	; man 2 exit arg1 == status
  	syscall
  .msg:
  	db	'Greetings, HackerNews!',10
  .msglen = $ - .msg
I don't think it gets much clearer or simpler than that, and more so that "man 2 xxx" provides all of the information you need for syscall goods anyway, it really isn't that bad :-)

if you compile this with fasm (http://www.flatassembler.net/) it produces a 174 byte ELF64 (and if we wanted to have some fun, we could lower that number by quite a bit still).

Cheers!

Edit: leading spaces for code


> and if we wanted to have some fun, we could lower that number by quite a bit still

One of my earliest introductions to the ELF format was "A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux" (http://www.muppetlabs.com/~breadbox/software/tiny/teensy.htm...), which I still highly recommend.


This is a beauty, thanks for sharing! I second the recommendation.


You're more than welcome, I was astounded by your great work when I saw it on the HN frontpage a week or two ago.

Since then, I have packaged heavything's whole showcase for Arch[1] (mainly because I'm quite interested in using rwasa for my own website but also so that other Archers may reap the benefits as well).

Keep up the awesome work!

[1] https://aur.archlinux.org/pkgbase/heavything


and if we wanted to have some fun, we could lower that number by quite a bit still

The most "immediately" (pun intended) obvious size optimisation would be to do this:

    push 1
    pop eax
    mov edi, eax
    ...
    push .msglen
    pop edx
    ...
    push 60
    pop eax
...which should save 9 bytes. Note that 174 bytes is still a far ways off from the equivalent under DOS, which would be...

    95 BA 07 01 CD 21 C3 47 72 65 65 74 69 6E 67 73
    2C 20 48 61 63 6B 65 72 4E 65 77 73 21 0D 0A 24
...these 32 bytes, 25 of which is the message itself.


From a beginners point of view, what's the benefit of fasm over gas?


Like markdown, 3+ leading spaces.

   mov rax, 0xdeadbeef


Much nicer, thanks!


Wait, ELF64 and a pointer going into a 32 bit register?


By default all code/RIP addresses are <32 bit, no harm no foul.


It also uses the int 0x80 interface instead of the faster sysenter opcode. (AMD calls this opcode syscall.)

http://wiki.osdev.org/SYSENTER

I say "faster" above, and that's true for all chips since the Pentium 4, if not earlier, but it isn't true on all 32-bit x86 chips. It isn't even true on all 32-bit x86 chips which have the opcode. So the Linux kernel has a special trick such that all binaries always use the fastest syscall method, regardless of which kind of system they were compiled for: linux-gate.so.1 also known as linux-vdso.so.1

This isn't actually a file on disk, it's just some machine code the kernel maps into the process address space which syscalls indirect through, because the kernel knows more about the hardware than applications.

http://www.trilithium.com/johan/2005/08/linux-gate/

vDSO even stands for virtual Dynamic Shared Object:

http://man7.org/linux/man-pages/man7/vdso.7.html

Aside from being virtual, it's a normal ELF shared object, with symbol names and versoning and the functions use the normal C calling convention.


On my list of things to do is to write an article titled "VDSO and why you should care." Interestingly, syscall indirection isn't among my reasons (maybe I am too comfortable in my SSE2+ x86_64 environment). If anyone is interested, the magic all lives in /proc/self/auxv, and my own library implementation to hook gettimeofday can be perused at:

https://2ton.com.au/library_as_html/vdso.inc.html



Awesome resources. Loving the Linux videos so far. Great instructor.


Can't agree more, the description of the different registers was very clear for me.


Oh man, that is awesome stuff. Thanks for sharing!


I tried out using x86_64 Linux assembly by writing a simple TCP echo server [1] after reading about the modern HTTP server in 64bit assembly [2]. Turned out to be not too much more involved than writing the equivalent in C, though very much at the cost of readability and development time.

There appeared to be relatively few examples of modern (well, 64 bit) Linux assembly out there, though quite a few relatively out of date documents like the one posted; documentation like the 64bit Linux System Call table were exceptionally useful [3].

[1] https://gist.github.com/bobbo/e1e980262f2ddc8db3b8

[2] https://news.ycombinator.com/item?id=9948749

[3] http://blog.rchapman.org/post/36801038863/linux-system-call-...


I've been meaning to learn some assembly for a while, but never quite knew where to start. Then I found this handy guide to writing "Hello, World" in assembly on Linux. Just install nasm and follow the instructions here and you're good to go.


Alternatively you can cut/paste the GAS syntax and use gcc.

   gcc -nostdlib hello.S -o hello
This example uses the old 32-bit syscall invocation mechanism, which works fine under 64-bit Linux. It might be a useful exercise to try porting this program to the 64-bit syscall convention.


Alternatively you can cut/paste the GAS syntax and use gcc.

Oh, interesting. I didn't know you could do that with gcc. Thanks for the tip!

This example uses the old 32-bit syscall invocation mechanism, which works fine under 64-bit Linux. It might be a useful exercise to try porting this program to the 64-bit syscall convention.

I wasn't familiar with the difference, but some googling turned up this SO discussion which seems relevant. Maybe I will try making the change and see if I can get this to run with pure 64 bit code.

http://stackoverflow.com/questions/8510333/x86-64-assembly-l...


You can extract the differences by looking at my librt0 [0] project. You can also see a minimal amd64 version in C here [1].

[0] https://github.com/lpsantil/rt0/blob/master/src/lib/syscall....

[1] https://gist.github.com/lunixbochs/462ee21c3353c56b910f


You can also see a minimal amd64 version in C here [1].

Ah, that is very handy, and very illustrative. Thanks for sharing!


I like Bartlett's Programming from the Ground Up, though it might be too simple for some. It's available online in a few places:

https://www.google.com/search?q=Bartlett+programming+from+th...


Shameless plug for those looking to ease their asm dev experience in ST3

https://packagecontrol.io/packages/NASM%20x86%20Assembly


I find these assembly demos that rely largely upon system calls lacking. I know much stuff cannot be done without system calls, but there's got to be a better "Hello, World" for assembly.


What would a "better" "Hello, World" for assembly look like?


Not necessarily "better", but lower level and without the use of syscalls: on bare-metal x86 protected mode, writing characters to "video memory" (from 0xB8000, normally).

http://wiki.osdev.org/Printing_To_Screen


IMO, bootloader/video memory is quite a distance from the traditional model of what "Hello World" is meant to be about, no? If anyone's interested, there are lots of very simple bootloader Hello World goods around:

http://board.flatassembler.net/topic.php?t=17130


I don't know, and that's part of the reason why I raised the issue.


If you wish to make an apple pie from scratch, you must first create the universe.


Mmm, universe.


$ nasm -f elf hello.asm

$ ld -o hello hello.o

ld: i386 architecture of input file `hello.o' is incompatible with i386:x86-64 output

$ nasm -f elf64 hello.asm

$ ld -o hello hello.o

$ ./hello

hello, friend



This makes me want to dig into assembly after all.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: