NASM Hello World on Linux: undefined reference to `main' - linux

I've been following this tutorial for an intro to assembly on Linux.
section .text
global _start ;must be declared for linker (ld)
_start:
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptior
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x080 ;call kernel
section .data
msg db 'Hello, world!', 0xa ;the string
len equ $ - msg ;length of the string
I've then had problems compiling it. I've looked around and found (on SO) that I should compile it like this:
nasm -f elf64 hello.asm
gcc -o hello hello.o
But I keep getting this error from GCC:
hello.o: In function `_start':
hello.asm:(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o:(.text+0x0): first defined here
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
(NB: I'm running Debian Linux on a 64 bit Intel i7)

If you are going to learn assembly, then you are much better served learning to use the assembler nasm and the linker ld without relying on gcc. There is nothing wrong with using gcc, but it masks part of the linking process that you need to understand going forward.
Learning assembly in the current environment (generally building on x86_64 but using examples that are written in x86 32-bit assembler), you must learn to build for the proper target and the language (syscall) differences between the two. Your code example is 32-bit assembler. As such your nasm compile string is incorrect:
nasm -f elf64 hello.asm
The -f elf64 attempts to compile a 64-bit object file, but the instructions in your code are 32-bit instructions. (It won't work)
Understanding and using ld provides a better understanding of the differences. Rather than using gcc, you can use nasm and ld to accomplish the same thing. For example (with slight modification to the code):
msg db 0xa, 'Hello, StackOverflow!', 0xa, 0xa ;the string
You compile and build with:
nasm -f elf -o hello-stack_32.o hello-stack_32.asm
ld -m elf_i386 -o hello-stack_32 hello-stack_32.o
Note the use of -f elf for 32-bit code in the nasm call and the -m elf_i386 linker option to create a compatible executable.
output:
Hello, StackOverflow!
If you are serious about learning assembler, there are a number of good references on the web. One of the best is The Art of Assembly. (it is written primarily for 8086 and x86, but the foundation it provides is invaluable). In addition, looking at the executables you create in binary can be helpful. Take a look at Binary Vi (BVI). It is a good tool.
bvi screenshot

You should add -nostdlib when linking your binary.
gcc -o hello hello.o -nostdlib

Related

Linking a program using printf with ld?

I'm getting a undefined reference to _printf when building an assembly program that defines its own _start instead of main, using NASM on x86-64 Ubuntu
Build commands:
nasm -f elf64 hello.asm
ld -s -o hello hello.o
hello.o: In function `_start':
hello.asm:(.text+0x1a): undefined reference to `_printf'
MakeFile:4: recipe for target 'compile' failed
make: *** [compile] Error 1
nasm source:
extern _printf
section .text
global _start
_start:
mov rdi, format ; argument #1
mov rsi, message ; argument #2
mov rax, 0
call _printf ; call printf
mov rax, 0
ret ; return 0
section .data
message: db "Hello, world!", 0
format: db "%s", 0xa, 0
Hello, World! should be the output
3 problems:
GNU/Linux using ELF object files does not decorate / mangle C names with a leading underscore. Use call printf, not _printf (Unlike MacOS X, which does decorate symbols with an _; keep that in mind if you're looking at tutorials for other OSes. Windows also uses a different calling convention, but only 32-bit Windows mangles names with _ or other decorations that encode the choice of calling convention.)
You didn't tell ld to link libc, and you didn't define printf yourself, so you didn't give the linker any input files that contain a definition for that symbol. printf is a library function defined in libc.so, and unlike the GCC front-end, ld doesn't include it automatically.
_start is not a function, you can't ret from it. RSP points to argc, not a return address. Define main instead if you want it to be a normal function.
Link with gcc -no-pie -nostartfiles hello.o -o hello if you want a dynamic executable that provides its own _start instead of main, but still uses libc.
This is safe for dynamic executables on GNU/Linux, because glibc can run its init functions via dynamic linker hooks. It's not safe on Cygwin, where its libc is only initialized by calls from its CRT start file (which do that before calling main).
Use call exit to exit, instead of making an _exit system call directly if you use printf; that lets libc flush any buffered output. (If you redirect output to a file, stdout will be full-buffered, vs. line buffered on a terminal.)
-static would not be safe; in a static executable no dynamic-linker code runs before your _start, so there's no way for libc to get itself initialized unless you call the functions manually. That's possible, but generally not recommended.
There are other libc implementations that don't need any init functions called before printf / malloc / other functions work. In glibc, stuff like the stdio buffers are allocated at runtime. (This used to be the case for MUSL libc, but that's apparently not the case anymore, according to Florian's comment on this answer.)
Normally if you want to use libc functions, it's a good idea to define a main function instead of your own _start entry point. Then you can just link with gcc normally, with no special options.
See What parts of this HelloWorld assembly code are essential if I were to write the program in assembly? for that and a version that uses Linux system calls directly, without libc.
If you wanted your code to work in a PIE executable like gcc makes by default (without --no-pie) on recent distros, you'd need call printf wrt ..plt.
Either way, you should use lea rsi, [rel message] because RIP-relative LEA is more efficient than mov r64, imm64 with a 64-bit absolute address. (In position-dependent code, the best option for putting a static address in a 64-bit register is 5-byte mov esi, message, because static addresses in non-PIE executables are known to be in the low 2GiB of virtual address space, and thus work as 32-bit sign- or zero-extended executables.
But RIP-relative LEA is not much worse and works everywhere.)
;;; Defining your own _start but using libc
;;; works on Linux for non-PIE executables
default rel ; Use RIP-relative for [symbol] addressing modes
extern printf
extern exit ; unlike _exit, exit flushes stdio buffers
section .text
global _start
_start:
;; RSP is already aligned by 16 on entry at _start, unlike in functions
lea rdi, [format] ; argument #1 or better mov edi, format
lea rsi, [message] ; argument #2
xor eax, eax ; no FP args to the variadic function
call printf ; for a PIE executable: call printf wrt ..plt
xor edi, edi ; arg #1 = 0
call exit ; exit(0)
; exit definitely does not return
section .rodata ;; read-only data can go in .rodata instead of read-write .data
message: db "Hello, world!", 0
format: db "%s", 0xa, 0
Assemble normally, link with gcc -no-pie -nostartfiles hello.o. This omits the CRT startup files that would normally define a _start that does some stuff before calling main. Libc init functions are called from dynamic linker hooks so printf is usable.
This would not be the case with gcc -static -nostartfiles hello.o. I included examples of what happens if you use the wrong options:
peter#volta:/tmp$ nasm -felf64 nopie-start.asm
peter#volta:/tmp$ gcc -no-pie -nostartfiles nopie-start.o
peter#volta:/tmp$ ./a.out
Hello, world!
peter#volta:/tmp$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0cd1cd111ba0c6926d5d69f9191bdf136e098e62, not stripped
# link error without -no-pie because it doesn't automatically make PLT stubs
peter#volta:/tmp$ gcc -nostartfiles nopie-start.o
/usr/bin/ld: nopie-start.o: relocation R_X86_64_PC32 against symbol `printf##GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
# runtime error with -static
peter#volta:/tmp$ gcc -static -no-pie -nostartfiles nopie-start.o -o static_start-hello
peter#volta:/tmp$ ./static_start-hello
Segmentation fault (core dumped)
Alternative version, defining main instead of _start
(And simplifying by using puts instead of printf.)
default rel ; Use RIP-relative for [symbol] addressing modes
extern puts
section .text
global main
main:
sub rsp, 8 ;; RSP was 16-byte aligned *before* a call pushed a return address
;; RSP is now 16-byte aligned, ready for another call
mov edi, message ; argument #1, optimized to use non-PIE-only move imm32
call puts
add rsp, 8 ; restore the stack
xor eax, eax ; return 0
ret
section .rodata
message: db "Hello, world!", 0 ; puts appends a newline
puts pretty much exactly implements printf("%s\n", string); C compilers will make this optimization for you, but in asm you should do it yourself.
Link with gcc -no-pie hello.o, or even statically link using gcc -no-pie -static hello.o. The CRT startup code will call glibc init functions.
peter#volta:/tmp$ nasm -felf64 nopie-main.asm
peter#volta:/tmp$ gcc -no-pie nopie-main.o
peter#volta:/tmp$ ./a.out
Hello, world!
# link error if you leave out -no-pie because of the imm32 absolute address
peter#volta:/tmp$ gcc nopie-main.o
/usr/bin/ld: nopie-main.o: relocation R_X86_64_32 against `.rodata' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: nonrepresentable section on output
collect2: error: ld returned 1 exit status
main is a function, so you need to re-align the stack before making another function call. A dummy push is also a valid way to align the stack on function entry, but add/sub rsp, 8 is clearer.
An alternative is jmp puts to tailcall it, so main's return value will be whatever puts returns. In this case, you must not modify rsp first: you just jump to puts with your return address still on the stack, exactly like if your caller had called puts.
PIE-compatible code defining a main
(You can make a PIE that defines its own _start. That's left as an exercise for the reader.)
default rel ; Use RIP-relative for [symbol] addressing modes
extern puts
section .text
global main
main:
sub rsp, 8 ;; RSP was 16-byte aligned *before* a call pushed a return address
lea rdi, [message] ; argument #1
call puts wrt ..plt
add rsp, 8
xor eax, eax ; return 0
ret
section .rodata
message: db "Hello, world!", 0 ; puts appends a newline
peter#volta:/tmp$ nasm -felf64 pie.asm
peter#volta:/tmp$ gcc pie.o
peter#volta:/tmp$ ./a.out
Hello, world!
peter#volta:/tmp$ file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b27e6032f955d628a542f6391b50805c68541fb9, not stripped

ld can not find symbol _start error after assemble and link .asm file to 64 bit executables

I have a shellcode file.
Then use ndisasm to build the assembly code.
ndisasm -b 64 shellcode > shellcode.asm
cat shellcode.asm | cut -c29->key.asm
I add 2 lines to the key.asm file
global_start:
_start:
$vi key.asm
global_start:
_start:
xor eax,eax
push rax
push qword 0x79237771
push qword 0x76772427
push qword 0x25747320
. . .
. . .
. . .
push qword 0x20757577
push rsp
pop rsi
mov edi,esi
mov edx,edi
cld
mov ecx,0x80
mov ebx,0x41
xor eax,eax
push rax
lodsb
xor eax,ebx
An then I assemble and link it to 64 bit executables
$nasm -f elf64 -g -F stabs key.asm
$ld -o key key.o
It gives me a warning
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400080
I tested it out with gcc
gcc -o key key.o
I still get an error almost the same as the first one
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: In
function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
And when I run ./key with gdb after I use $ld NOT $gcc
$gdb -q ./key
$run
I get a seg fault
Starting program: /mnt/c/Users/owner/Documents/U
M/Computer_Security/ExtraCredit/key
Program received signal SIGSEGV, Segmentation fault.
0x000000000040013a in global_start ()
If I debug after run with gcc then the file will not be found because of exit status
Can you explain why does it happen? And how can I fix this problem? Thanks
It gives me a warning
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400080
This isn't actually a problem, as long as you're fine with the entry point being the start of the text segment (i.e. the first instruction in your shellcode).
You got the error because you left out the space between the global keyword and the _start symbol name. i.e. use global _start, or don't bother. What you did defined a label called global_start, as you can see from your later error message.
You segfault on lodsb because you truncated a stack address with mov edi,esi instead of mov rdi, rsi. And if you fix that then you fall off the end of your code into garbage instructions because you don't make an exit system call. You're already running this inside gdb, use it!
I tested it out with gcc: gcc -o key key.o
I still get an error almost the same as the first one
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o:
In function `_start':
(.text+0x20): undefined reference to `main'
No, that's a totally different error. If you had exported _start correctly, you would have gotten an error for conflicting definitions of _start (between your code and the CRT start files).
This error is that the _start definition in crt1.o (provided by gcc) has a reference to main, but your code doesn't provide a main. This is what happens when you try to compile a C or C++ program that doesn't define main.
To link with gcc, use -nostdlib to omit the CRT start files and all other libraries. (i.e. link pretty much exactly like you were doing manually with ld.)
gcc -nostdlib -static key.o -o key # static executable: just your code
Or dynamically linked without the CRT start files, using your _start.
gcc -nostdinc -no-pie key.o -o key
You can call libc functions from code linked that way, but only on Linux or other platforms where dynamic linking takes care of running libc initialization functions.
If you statically link libc, you can only call functions like printf if you first call all the libc init functions that the normal CRT startup code does. (Not going into detail here because this code doesn't use libc)
Your code is wrong. Between global and _start must have a space. That is one of your problems.
section .text
global _start
_start:
xor eax,eax
push rax
...
In addition, to get why the segmentation fault is happening, you have to debug it. You could look the assembly instruction that does the segfault.
x/5i $eip

How to link the C Runtime Library with 'ld'?

I'm learning assembly with NASM for a class I have in college. I would like to link the C Runtime Library with ld, but I just can't seem to wrap my head around it. I have a 64 bit machine with Linux Mint installed.
The reason I'm confused is that -- to my knowledge -- instead of linking the C runtime, gcc copies the things that you need into your program. I might be wrong though, so don't hesitate to correct me on this, please.
What I did up to this point is, to link it using gcc. That produces a mess of a machine code that I'm unable to follow though, even for a small program like swapping rax with rbx, which isn't that great for learning purposes. (Please note that the program works.)
I'm not sure if it's relevant, but these are the commands that I'm using to compile and link:
# compilation
nasm -f elf64 swap.asm
# gcc
gcc -o swap swap.o
# ld, no c runtime
ld -s -o swap swap.o
Thank you in advance!
Conclusion:
Now that I have a proper answer to the question, here are a few things that I would like to mention. Linking glibc dynamically can be done like in Z boson's answer (for 64 bit systems). If you would like to do it statically, do follow this link (that I'm re-posting from Z boson's answer).
Here's an article that Jester posted, about how programs start in linux.
To see what gcc does to link your .o-s, try this command out: gcc -v -o swap swap.o. Note that 'v' stands for 'verbose'.
Also, you should read this if you are interested in 64 bit assembly.
Thank you for your answers and helpful insight! End of speech.
Here is an example which uses libc without using GCC.
extern printf
extern _exit
section .data
hello: db 'Hello world!',10
section .text
global _start
_start:
xor eax, eax
mov edi, hello
call printf
mov rax, 0
jmp _exit
Compile and link like this:
nasm -f elf64 hello.asm
ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -m elf_x86_64
This has worked fine so far for me but for static linkage it's complicated.
If you want to call simple library functions like atoi, but still avoid using the C runtime, you can do that. (i.e. you write _start, rather than just writing a main that gets called after a bunch of boiler-plate code runs.)
gcc -o swap -nostartfiles swap.o
As people say in comments, some parts of glibc depend on constructors/destructors run from the standard startup files. Probably this is the case for stdio (puts/printf/scanf/getchar), and maybe malloc. A lot of functions are "pure" functions that just process the input they're given, though. sprintf/sscanf might be ok to use.
For example:
$ cat >exit64.asm <<EOF
section .text
extern exit
global _start
_start:
xor edi, edi
jmp exit ; doesn't return, so optimize like a tail-call
;; or make the syscall directly, if the jmp is commented
mov eax, 231 ; exit(0)
syscall
; movl eax, 1 ; 32bit call
; int 0x80
EOF
$ yasm -felf64 exit64.asm && gcc -nostartfiles exit64.o -o exit64-dynamic
$ nm exit64-dynamic
0000000000601020 D __bss_start
0000000000600ec0 d _DYNAMIC
0000000000601020 D _edata
0000000000601020 D _end
U exit##GLIBC_2.2.5
0000000000601000 d _GLOBAL_OFFSET_TABLE_
00000000004002d0 T _start
$ ltrace ./exit64-dynamic
enable_breakpoint pid=11334, addr=0x1, symbol=(null): Input/output error
exit(0 <no return ...>
+++ exited (status 0) +++
$ strace ... # shows the usual system calls by the runtime dynamic linker

Error when trying to run .asm file on NASM on Ubuntu

I'm using ubuntu 64-bit and trying to run a .asm file on NASM. But it returns this error when I try to run the following code. What Iḿ trying to do is build an executable by compiling (or assembling) object file from the source
$ nasm -f elf hello.asm, and then after created the file hello.o is producing executable file itself from the object file by invoking linker
$ ld -s -o hello hello.o
This will finally build hello executable.
I'm following this tutorial http://www.faqs.org/docs/Linux-HOWTO/Assembly-HOWTO.html
Error:
i386 architecture of input file `hello.o' is incompatible with i386:x86-64 output
Code:
section .data ;section declaration
msg db "Hello, world!",0xa ;our dear string
len equ $ - msg ;length of our dear string
section .text ;section declaration
;we must export the entry point to the ELF linker or
global _start ;loader. They conventionally recognize _start as their
;entry point. Use ld -e foo to override the default.
_start:
;write our string to stdout
mov edx,len ;third argument: message length
mov ecx,msg ;second argument: pointer to message to write
mov ebx,1 ;first argument: file handle (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
;and exit
mov ebx,0 ;first syscall argument: exit code
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
This looks like it may be a simple mismatch between what's produced by nasm and what ld is trying to make:
i386 architecture of input file 'hello.o' is incompatible with i386:x86-64 output
In other words, nasm has produced a 32-bit object file hello.o and ld wants to take that and make a 64-bit executable file.
The nasm -hf command should give you the available output formats:
valid output formats for -f are (`*' denotes default):
* bin flat-form binary files (e.g. DOS .COM, .SYS)
ith Intel hex
srec Motorola S-records
aout Linux a.out object files
aoutb NetBSD/FreeBSD a.out object files
coff COFF (i386) object files (e.g. DJGPP for DOS)
elf32 ELF32 (i386) object files (e.g. Linux)
elf ELF (short name for ELF32)
elf64 ELF64 (x86_64) object files (e.g. Linux)
as86 Linux as86 (bin86 version 0.3) object files
obj MS-DOS 16-bit/32-bit OMF object files
win32 Microsoft Win32 (i386) object files
win64 Microsoft Win64 (x86-64) object files
rdf Relocatable Dynamic Object File Format v2.0
ieee IEEE-695 (LADsoft variant) object file format
macho32 NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X (i386) object files
macho MACHO (short name for MACHO32)
macho64 NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X (x86_64) object files
dbg Trace of all info passed to output stage
I see that your linked tutorial asks you to run:
nasm -f elf hello.asm
Try using:
nasm -f elf64 hello.asm
instead, and you may find ld stops complaining about the input file.
You need to tell the linker to produce an i386 output file, since you're writing i386 assembly:
ld -m elf_i386 -s -o hello hello.o
How to compile, link, and run a nasm app on Ubuntu 64 bit.
Install nasm:
sudo apt-get install nasm
Save a file with filename hello.asm:
section .data
hello: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
helloLen: equ $-hello ; Length of the 'Hello world!' string
; (I'll explain soon)
section .text
global _start
_start:
mov eax,4 ; The system call for write (sys_write)
mov ebx,1 ; File descriptor 1 - standard output
mov ecx,hello ; Put the offset of hello in ecx
mov edx,helloLen ; helloLen is a constant, so we don't need to say
; mov edx,[helloLen] to get it's actual value
int 80h ; Call the kernel
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return code of 0 (no error)
int 80h
Compile it:
nasm -f elf64 hello.asm
Link it:
ld -s -o hello hello.o
Run it
el#apollo:~$ ./hello
Hello world!
It works! What now? Request that your favorite compiler generate the assembly code that it would have been normally passed on to be converted to machine code. Google search: "convert php/java/python/c++ program to assembly"
Perspective: With all the people today attempting to tear down and get rid of general purpose computing for the general public, it's imperative that we teach the new students the concepts of how to build a general purpose turing machine from core principles, on up through the bare metal, then finally assemblers and programming languages.
How does learning assembly aid in programming?
99% of computer programs out there are 10 to 100 times slower than they could optimized to be only because programmers don't know what delays are being forced on them by their favorite high level compiler or interpreter.
A thorough understanding of the full stack here means you can coerce your programs to have that coveted property of only taking nanoseconds to do the job at hand. Time == money. So this knowledge of how to shun anything that takes longer than a few nanoseconds to complete saves time, and therefore money.
https://softwareengineering.stackexchange.com/questions/156722/how-does-learning-assembly-aid-in-programming

Compile/run assembler in Linux?

I'm fairly new to Linux (Ubuntu 10.04) and a total novice to assembler. I was following some tutorials and I couldn't find anything specific to Linux.
So, my question is, what is a good package to compile/run assembler and what are the command line commands to compile/run for that package?
The GNU assembler is probably already installed on your system. Try man as to see full usage information. You can use as to compile individual files and ld to link if you really, really want to.
However, GCC makes a great front-end. It can assemble .s files for you. For example:
$ cat >hello.s <<"EOF"
.section .rodata # read-only static data
.globl hello
hello:
.string "Hello, world!" # zero-terminated C string
.text
.global main
main:
push %rbp
mov %rsp, %rbp # create a stack frame
mov $hello, %edi # put the address of hello into RDI
call puts # as the first arg for puts
mov $0, %eax # return value = 0. Normally xor %eax,%eax
leave # tear down the stack frame
ret # pop the return address off the stack into RIP
EOF
$ gcc hello.s -no-pie -o hello
$ ./hello
Hello, world!
The code above is x86-64. If you want to make a position-independent executable (PIE), you'd need lea hello(%rip), %rdi, and call puts#plt.
A non-PIE executable (position-dependent) can use 32-bit absolute addressing for static data, but a PIE should use RIP-relative LEA. (See also Difference between movq and movabsq in x86-64 neither movq nor movabsq are a good choice.)
If you wanted to write 32-bit code, the calling convention is different, and RIP-relative addressing isn't available. (So you'd push $hello before the call, and pop the stack args after.)
You can also compile C/C++ code directly to assembly if you're curious how something works:
$ cat >hello.c <<EOF
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0;
}
EOF
$ gcc -S hello.c -o hello.s
See also How to remove "noise" from GCC/clang assembly output? for more about looking at compiler output, and writing useful small functions that will compile to interesting output.
The GNU assembler (gas) and NASM are both good choices. However, they have some differences, the big one being the order you put operations and their operands.
gas uses AT&T syntax (guide: https://stackoverflow.com/tags/att/info):
mnemonic source, destination
nasm uses Intel style (guide: https://stackoverflow.com/tags/intel-syntax/info):
mnemonic destination, source
Either one will probably do what you need. GAS also has an Intel-syntax mode, which is a lot like MASM, not NASM.
Try out this tutorial: http://asm.sourceforge.net/intro/Assembly-Intro.html
See also more links to guides and docs in Stack Overflow's x86 tag wiki
If you are using NASM, the command-line is just
nasm -felf32 -g -Fdwarf file.asm -o file.o
where 'file.asm' is your assembly file (code) and 'file.o' is an object file you can link with gcc -m32 or ld -melf_i386. (Assembling with nasm -felf64 will make a 64-bit object file, but the hello world example below uses 32-bit system calls, and won't work in a PIE executable.)
Here is some more info:
http://www.nasm.us/doc/nasmdoc2.html#section-2.1
You can install NASM in Ubuntu with the following command:
apt-get install nasm
Here is a basic Hello World in Linux assembly to whet your appetite:
http://web.archive.org/web/20120822144129/http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html
I hope this is what you were asking...
There is also FASM for Linux.
format ELF executable
segment readable executable
start:
mov eax, 4
mov ebx, 1
mov ecx, hello_msg
mov edx, hello_size
int 80h
mov eax, 1
mov ebx, 0
int 80h
segment readable writeable
hello_msg db "Hello World!",10,0
hello_size = $-hello_msg
It comiles with
fasm hello.asm hello
My suggestion would be to get the book Programming From Ground Up:
http://nongnu.askapache.com/pgubook/ProgrammingGroundUp-1-0-booksize.pdf
That is a very good starting point for getting into assembler programming under linux and it explains a lot of the basics you need to understand to get started.
The assembler(GNU) is as(1)
3 syntax (nasm, tasm, gas ) in 1 assembler, yasm.
http://www.tortall.net/projects/yasm/
For Ubuntu 18.04 installnasm . Open the terminal and type:
sudo apt install as31 nasm
nasm docs
For compiling and running:
nasm -f elf64 example.asm # assemble the program
ld -s -o example example.o # link the object file nasm produced into an executable file
./example # example is an executable file

Resources