ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Hi, I haven't done much assembler programming (n00b) and I was wondering if I could get some help. I want to write this program that prints off the arguments that are (would be in C at least) pointed to by the char **argv array. This is what I would like the output to say to the following line.
$ ./a.out a b c
argv[0]=./a.out
argv[1]=a
argv[2]=b
argv[3]=c
The following code works fine when no argument is specified, but segfaults when there is at least one argument. I don't have access to my home computer at the moment, so I thought I'd let smarter people than myself figure this out. I'm new to this so be gentle.
Quote:
.data
FMT:
.string "argc[%d]=%s\n"
.text
.globl _start
.extern puts
_start:
pop %r8 #argc into r8
mov $0x0, %rsi #0 into rsi
Loop1:
cmp %r8, %rsi
jge Loop1Out #if rsi>=r8 jump out
mov $FMT, %rdi #make sure FMT is in rdi
pop %rdx #pop an argv off the stack
push %r8
push %rsi
call printf #printf;
pop %rsi
pop %r8
inc %rsi
jmp Loop1
Loop1Out:
pushq $0x0
pop %rdi
pushq $0x3c # exit(3c)
pop %rax
syscall
For starters, you didn't even mention kind of assembly language this is (Power PC? it certainly doesn't look like Intel x86...) or what platform it's running on (presumably Linux?). That would drastically affect whether instructions like "pop %r8" or "pop %rdx" are appropriate methods for accessing "argv[]".
But why don't you try this:
a) Write a C "hello world" that accesses argc/argv[]
b) compile with "gcc -S -O0"
c) analyze the resulting assembly source file
For starters, you didn't even mention kind of assembly language this is (Power PC? it certainly doesn't look like Intel x86...)
Um, well, presumably x86_64 since the title of the thread is "x86_64 assembly help".
At first glance, the problem appears to be that you didn't decrement r8 (argc) at the start. Remember that argc also includes the name of the program. Without the dec, you are using the terminating NULL as an argument which is presumably what is causing the segfault (when passed to printf). Speaking of terminating NULLs, you could just test for that instead of counting the indices, i.e. cmp rdx to 0[1] and break when it's equal.
[1] See the C usenet FAQ for a discussion of the potential for confusion surrounding null pointers. A null pointer is not necessarily binary zero on some architectures, though for x86, it is safe to assume that it is. In C it doesn't matter, since it's the responsibility of the compiler to translate a NULL (or bare 0) to the correct format for the particular architecture. In assembly of course, you'll probably need to do this yourself.
OK - sorry. I was looking at the code in your question, and didn't pay any attention to the "x86_64 assembly" part of the question.
As it turns out, the "gcc -S" suggestion I gave wasn't too great, either. I tried it myself on my AMD/64, SusE Linux 10.x system: my output doesn't look anything *remotely* like your code. So "whoops" on that one, too.
I'm still curious: what kind of CPU (AMD/64? Intel/64? Ithanium?) and assembler (GNU AS? Intel assembler?) do you have? Which version of Linux (or, if not Linux, which OS)?
$ uname -a
Linux helios 2.6.18-gentoo-r6 #6 Mon Jan 15 22:52:44 EST 2007 x86_64 AMD Athlon(tm) 64 Processor 3700+ AuthenticAMD GNU/Linux
using gas as the assembler and ld (through gcc) to link.
$ as -g --64 test.s -o test.o
$ gcc -g test.o -o test -nostartfiles
Yeah the assembly that gcc generates for x86_64 is kinda scary to me, but perhaps I should endeavor to emulate it more closely.
As I understand if I run "./a.out a b c d" then the stack should look like this...
----top
0 (NULL)
pointer to the C string "d"
'' "c"
'' "b"
'' "a"
'' "./a.out"
4
----bottom
Is this correct?
Here's the corresponding C for what I want to do...
Quote:
#include <stdio.h>
int main(int argc, char **argv) {
int i;
for (i=0; i<argc; ++i) {
printf("argv[%d]=%s\n", i, argv[i]);
}
}
I actually may have some time to work on this today. Testing for the NULL at the end seems like a much better idea that what I'm currently doing. Thanks for the help so far.
EDIT:
Almost forgot...
GNU assembler version 2.16.1 (x86_64-pc-linux-gnu) using BFD version 2.16.1
GNU ld version 2.16.1
gcc version 4.1.1 (Gentoo 4.1.1-r1)
Last edited by tomtechguy; 02-04-2007 at 11:00 AM.
OK - sorry. I was looking at the code in your question, and didn't pay any attention to the "x86_64 assembly" part of the question.
Heh, funny old thing the human brain. I can read a piece of text then immediately forget every word, yet I can still remember the diameter of Jupiter from looking it up twenty years ago. ?
Quote:
Originally Posted by paulsm4
As it turns out, the "gcc -S" suggestion I gave wasn't too great, either. I tried it myself on my AMD/64, SusE Linux 10.x system: my output doesn't look anything *remotely* like your code. So "whoops" on that one, too.
An excellent way to learn assembly language. This is how I learnt back when I was using DOS.
Note though, that gcc's default (un-optimized) output is relatively inefficient, try using -O2 and comparing it with the default output to see how gcc improves the basic code.
Quote:
Originally Posted by tomtechguy
As I understand if I run "./a.out a b c d" then the stack should look like this...
----top
0 (NULL)
pointer to the C string "d"
'' "c"
'' "b"
'' "a"
'' "./a.out"
4
----bottom
Is this correct?
Yes, that's correct, the parameters go on the stack in reverse order, last one at the top (well, after the NULL) and so on. Thus argc is at the bottom of the stack. You just need to remember to ignore argv[0] and you should be OK.
Last edited by iamnothere; 02-04-2007 at 02:26 PM.
For whatever it's worth:
1. I'm able to find and print "argc" (it's at -4(%rsp))
2. I believe I can access "argv" (but I can't print it yet)
3. I'm able to call C library "printf" (last time I looked, it took arguments off the stack. My current version of GCC now allocates registers instead)
4. I'm able to make Linux system calls (similarly, the last time I looked, Linux system calls were invoked via "int 0x80". Under Linux/64, they're instead invoked via "syscall", and the call numbers and parameters are completely changed from Linux/32).
Here's my code:
Code:
#
# ASM/Linux "Hello world"
#
# REFERENCES:
# - 32-bit "print cmd-line args" example:
# http://asm.sourceforge.net/articles/linasm-src.html#args
# <= Obsolete: GCC "printf()" now called with register args instead of stack;
# 32-bit "int $0x80" replaced with "syscall"
# Linux/64 API call #'s completely different
#
# - x86-64 ABI:
# http://www.x86-64.org/documentation/abi.pdf
#
# - Differences between 32-bit and 64-bit assembly:
# http://www.milw0rm.com/papers/110
#
# - Changes from sourceforge.net example:
# - Changed comment symbols from "//" to "#"
# - Changed 32-bit "l" qualifiers and "%exx" opcodes to 64-bit "q"/"%rxx"
# - Etc etc
#
.equ SYS_exit,60 # SYS_exit formerly "1" under "int $0x80" Linux/32 ABI
.equ SYS_write,1 # SYS_write formerly "4"
.equ STDOUT,1
.section .data
FMT1:
.asciz "argc: %d\n"
ENDL:
.asciz "\n"
.section .text
.globl main
main:
# Saver buffer pointer
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
# Get and print argc
# printf(FMT1, argc);
movl -4(%rbp), %esi
movq $FMT1, %rdi
xor %eax, %eax
call printf
# Make sure argc >= 2
movl -4(%rbp), %eax
cmp $2, %eax
jl exit
# Try to print 1st letter of argv[1]
movq -16(%rbp), %rax
addq $8, %rax
movq (%rax), %rsi
mov $1, %rax # OS call = SYS_write
mov $1, %edi # fd = 1 (stdout)
mov $1, %edx # len= 1
# %rsi = string pointer
syscall
exit:
mov $SYS_exit, %rax
mov $0, %rdi
syscall
ret
"hello.asm64.s" is the program above. "x.c" is a temp file I've been modifying and compiling with "gcc -m64 -O0 -S" to try to figure out how things work in this Brave New 64-bit world ;-)
I hope that's at least a *little" bit of help to you ... and I'm curious what you come up with when you get it completely working.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.