You are not logged in.

#1 2011-05-24 04:53:27

MrCode
Member
Registered: 2010-02-06
Posts: 373

[SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

Hey everyone,

I've recently started trying out asm (bored; mostly I'm doing this for educational purposes…might be useful in a C/C++ context, even if I never hand-write any asm other than this kind of basic proof-of-concept material), and I can't seem to get my first "Hello, World!" program to link properly.  It assembles, fine, though:

[mrcode@lappy486 Programs]$ yasm -f elf64 -o asm64-helloworld.o asm64-helloworld.S
[mrcode@lappy486 Programs]$ ld -o asm64-helloworld asm64-helloworld.o
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
asm64-helloworld.o: In function `_main':
asm64-helloworld.S:(.text+0x6): undefined reference to `_printf'
[mrcode@lappy486 Programs]$

Source (I translated it to 64-bit from this tutorial; not sure if doubling the amount to add to the stack pointer after pushing the string address onto the stack and calling printf is the right thing to do, but I assumed that the value would double since I'm working with a 64-bit/8-byte address/register as opposed to 32-bit/4-byte, as in the tutorial's code hmm):

[section .data]

    string: db 'Hello, World!',10,0
    
[section .text]

    global _main
    extern _printf
    
_main:
    push string
    call _printf
    add rsp,8

    mov rax,0
    ret

I've done some web searching on the issue, and I've tried using other ld options (e.g. -lc, -static, etc.) and using gcc (gcc -o asm64-helloworld asm64-helloworld.o), but nothing so far has worked.

I have no idea where to put _start, or where to refer to it (yes, I know, dumb n00b alert).  I've alredy tried declaring it as another global, and changing the _main label to _start, but of course neither solution works (I have a feeling that's kind of a dumb n00bish first guess anyway roll).

I can write C just fine; I've done a little tinkering with SDL recently, and I'm fairly comfortable with the basic C stdio functions (and I have the man pages if I forget/don't know tongue), but when it comes to writing raw asm code, I guess I'm just way too used to having gcc do all the work for me WRT assembly/linking. sad

EDIT: The tutorial assumes Windows…could that be at least part of the problem?  It uses the C standard library for printf, though, so I assumed it'd be at least a little portable. hmm  Perhaps that may have something to do with why ld is complaining about _start?  Different code entry points under different OSes?

Last edited by MrCode (2011-05-24 17:40:47)

Offline

#2 2011-05-24 05:21:10

sand_man
Member
From: Australia
Registered: 2008-06-10
Posts: 2,164

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

I just tried it but I used gcc to link it and it works.

$ yasm -felf -o hello.o hello.asm
$ gcc hello.o
$ ./a.out
$ Hello, World!

Oh sorry I used an example from the website which is a little different to yours
http://www.tortall.net/projects/yasm/at … oWorld.png

Last edited by sand_man (2011-05-24 05:22:48)


neutral

Offline

#3 2011-05-24 05:27:40

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 859
Website

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

Yeah the Windows/Linux difference is the problem here.  On Linux, you should call the function main (not _main), and call printf (not _printf).  Then, when linking, link with "gcc asm64-helloworld.so", which is significantly easier than

ld -o asm64-helloworld /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../lib/crt1.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../lib/crti.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/crtbegin.o -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0 -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../.. asm64-helloworld.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/crtend.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../lib/crtn.o

As written, it still segfaults, but that's because you're using the wrong calling convention.  Read the x86-64 ABI for details.  Here's a corrected example:

[section .data]

    string: db 'Hello, World!',10,0

[section .text]

    global main
    extern printf

main:
    mov rdi,string
    mov rax,0
    call printf

    mov rax,0
    ret

Offline

#4 2011-05-24 07:49:43

MrCode
Member
Registered: 2010-02-06
Posts: 373

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

@tavianator

Thanks for the info!

Your example works perfectly…here's how I assembled/linked it:

[mrcode@lappy486 Programs]$ yasm -f elf64 -o asm64-helloworld.so asm64-helloworld.S
[mrcode@lappy486 Programs]$ gcc -ggdb -o asm64-helloworld asm64-helloworld.so
[mrcode@lappy486 Programs]$

(-ggdb is there from seeing if I could get it to produce debugging symbols for gdb so I could trace it if it failed as I tinkered with it, but apparently that only works when you're compiling from a higher-level language [gdb didn't find any debugging symbols])

Don't worry, I didn't just do a copypaste and claim success. wink  I went ahead and took a look at the ABI document as well, and my understanding of what's going on in main is this:

  • mov the pointer to our char array ("string") into rdi for passing to printf because it's of the INTEGER class (includes char)

  • null out rax because we aren't passing any vector args

  • call the printf function

  • (best guess; correct me if I'm wrong) null rax again because main doesn't take any vector args

  • return from main

^ If that's all right, then I'll be amazed that I was able to understand what I could in that document. yikes  I'm just a beginner, and I'm already using the full reference docs for learning purposes, instead of just a simple beginner's tutorial!

In all seriousness though, thanks for the help.  I guess that means the tutorial I was looking at is kinda rendered null and void, then.  I figured there'd be more to it than just transposing register names and pointer lengths. tongue

Oh well, I can always hack on the code I have now to see what happens…

Last edited by MrCode (2011-05-24 07:52:44)

Offline

#5 2011-05-24 14:25:56

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 859
Website

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

Very good, you're close to the right interpretation.

  • Yes, rdi is used to pass the first argument of INTEGER class, which is a const char * in this case

  • null out rax because we aren't passing any vector args

  • call the printf function

  • * null rax again to return 0 from main

  • return from main

You'll need to pass "-g dwarf2" (see man yasm_dbgfmts) to get debugging information in the executable, but that's not really helpful when you're coding assembly.  Just use "disass" to see the disassembly of the current function in gdb.

By the way, a faster way to zero a register is "xor eax,eax".  This works because a xor a is 0 for any a, and when working with the 32-bit halves of 64-bit registers, the other 32 bits are cleared.  The instruction is shorter because it doesn't need to encode the immediate 0 value, and xor is faster than sub because most processors can skip the data dependency on eax for "xor eax,eax" but not "sub eax,eax".  You'll see that pattern all the time in the output of gcc -O[123] -S.

Last edited by tavianator (2011-05-24 15:06:11)

Offline

#6 2011-05-24 17:40:31

MrCode
Member
Registered: 2010-02-06
Posts: 373

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

Very good, you're close to the right interpretation.

[…]

  • * null rax again to return 0 from main

Ah, I had a suspicion that that was the case, but thanks for clearing that up. smile

You'll need to pass "-g dwarf2" (see man yasm_dbgfmts) to get debugging information in the executable, but that's not really helpful when you're coding assembly.  Just use "disass" to see the disassembly of the current function in gdb.

…so I guess I was right about the whole "debugging symbols are primarily only useful in higher-level languages" thing.  I only barely know how to use gdb, though, so "disass" isn't going to work for me (I don't know what address to give it as a param, which is what it wants).  I'm more used to debugging the "hard way": putting printfs in problem spots in the code to get output (or a lack thereof) when a piece of code is(n't) getting executed when it is(n't) supposed to.  I can always learn, though, and I suppose I'll have to if I want to do anything much more with asm, seeing as how in a lot of cases I would imagine that printf() never gets called if something on a deeper fundamental level goes wrong. tongue

By the way, a faster way to zero a register is "xor eax,eax".  This works because a xor a is 0 for any a, and when working with the 32-bit halves of 64-bit registers, the other 32 bits are cleared. […] You'll see that pattern all the time in the output of gcc -O[123] -S.

Well, I guess that would explain all the xoring I've seen in my messing with gcc -S <C source>.  I didn't know that xoring only the lower 32 bits of the register would implicitly null the upper 32 bits as well, though.

…and about the optimization/instruction speed thing: I learn more by example; code first, ask questions later. big_smile  IOW, as much as I'm sure I'll need and/or want to know the intricacies of the CPU instruction execution pipeline (at least as far as speed goes), I have a feeling that just being able to manage the registers at all is still a decent first step.  "mov rax,0" is just as good as "xor eax,eax" for me for now (and it's more immediately obvious to a newbie), but I did go ahead and changed both instances of the former to the latter.

Now I'm experimenting with loops and jump conditions. big_smile  I'll mark this thread [SOLVED], since technically it really is, and I've already got started on some other stuff.  Thanks for the help!

Last edited by MrCode (2011-05-24 17:42:05)

Offline

#7 2011-05-24 19:40:43

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 859
Website

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

MrCode wrote:

…so I guess I was right about the whole "debugging symbols are primarily only useful in higher-level languages" thing.  I only barely know how to use gdb, though, so "disass" isn't going to work for me (I don't know what address to give it as a param, which is what it wants).

Do bt to get a backtrace, frame <#> to select a stack frame, then disass with no arguments will show the disassembly around the current code location.  "info reg" is the other useful command, that dumps the values of all the registers.

Well, I guess that would explain all the xoring I've seen in my messing with gcc -S <C source>.  I didn't know that xoring only the lower 32 bits of the register would implicitly null the upper 32 bits as well, though.

All 32-bit instructions (instructions that target a 32-bit register, including the extended ones like r8d) zero-extend the result, clearing the high 32 bits of the target register.  This is somewhat counter-intuitive but it allows for smaller code in many cases.

…and about the optimization/instruction speed thing: I learn more by example; code first, ask questions later. big_smile  IOW, as much as I'm sure I'll need and/or want to know the intricacies of the CPU instruction execution pipeline (at least as far as speed goes), I have a feeling that just being able to manage the registers at all is still a decent first step.  "mov rax,0" is just as good as "xor eax,eax" for me for now (and it's more immediately obvious to a newbie), but I did go ahead and changed both instances of the former to the latter.

Fair enough.  This is sometimes a good resource if you're trying to figure out what gcc has done with your code (a little old though, and in 32-bit).

Now I'm experimenting with loops and jump conditions. big_smile  I'll mark this thread [SOLVED], since technically it really is, and I've already got started on some other stuff.  Thanks for the help!

No problem!

Offline

#8 2011-05-25 01:27:10

sand_man
Member
From: Australia
Registered: 2008-06-10
Posts: 2,164

Re: [SOLVED] Trying my hand at asm, not succeeding (ld/stdlib(?) problems)

The only assembly I've done is an assignment I had to do for uni in M68000 assembly. Probably the most fun I've had programming and the most rewarding too.
Although not everything is fun. Manually calculating floating point numbers can be painful (and probably useless in the real world).


neutral

Offline

Board footer

Powered by FluxBB