I have written a simple .c program, and tried to convert it by using gcc -S
and running it with:
but im not sure how to code my program in this format, if anyone could suggest a solution or help would be greatly appreciated.
I have written a simple .c program, and tried to convert it by using gcc -S
and running it with:
but im not sure how to code my program in this format, if anyone could suggest a solution or help would be greatly appreciated.
/lib/ld-linux.so.2
is the 32-bit dynamic linker; that's why you get the confusing "no such file or directory" when the shell tries to execve()
.
Use one of these commands to assemble+link 64-bit asm:
gcc -g -static -nostdlib start.s
(no CRT or libc, static executable). Use this for your Hello World that uses syscall
directly, with no call
s to glibc functions.gcc -g -no-pie -nostartfiles start.s
(no CRT, but does link libc, making a dynamic executable. Dynamic linker tricks on Linux allow the glibc init functions to run before execution reaches your _start
entry point, so you can still call printf
. Unlike if you made a static executable that linked libc.)gcc -g -no-pie main.s
(you write a main
instead of a _start
, exactly like a C compiler would produce)Add a -o myprog
option if you want a name other than a.out
.
To assemble + link 32-bit code, add -m32
to any of these.
Add a -v
option to see what commands the gcc front-end uses under the hood, if you for some reason want to invoke as
and ld
manually. gcc
on your system will know the right paths for libc and the dynamic linker. These will be different for 32 vs. 64-bit code.
-g
is there to create debug info, to make it easier to use GDB if you're using source view instead of asm view. See the bottom of the x86 tag wiki for asm gdb tips.
That said, this works on my system, and I think that dynamic linker path is pretty standard for modern x86-64 Linux systems. It's equivalent to gcc -no-pie -nostartfiles start.s
as -o file.o file.s &&
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o file -lc file.o
If you were using NASM or any other method to create a .o
, pass it to gcc instead of an asm source file. (e.g. nasm -felf64 foo.asm && gcc -static -nostdlib foo.o -o foo
). See also Assembling 32-bit binaries on a 64-bit system (GNU toolchain) for a lot more details.
-no-pie
allows 32-bit absolute addresses to work in 64-bit mode, so code like mov $message, %rsi
(which assembles to movq $sign_extended_imm32, %r64
, not movabsq $imm64, %r64
) to work. Difference between movq and movabsq in x86-64
-no-pie
also lets you write call puts
instead of call puts@plt
or a memory-indirect call like gcc -fno-plt
would use: call puts@GOTPCREL(%rip)
. The linker will take care of rewriting call puts
to call puts@plt
when it finds puts
in -lc
.
There's no reason you should ever write mov $message, %rsi
: either use mov $message, %esi
to take advantage of static addresses being in the low 32 bits of virtual address space, or use lea message(%rip), %rsi
to make position-independent code that works efficiently even if your code is loaded outside the low 32.
If you were compiling C, you'd want to use -fno-pie -no-pie
so compiler code-gen can take advantage of mov $message, %esi
and mov array(,%rax,4), %ecx
.
This answer only attempts to answer the question about how to build the code, which the screenshots show. For reading the compiler output to understand what it's doing, see How to remove "noise" from GCC/clang assembly output?.
And in this specific case of %100
, Why does GCC use multiplication by a strange number in implementing integer division?