Relocation error when compiling NASM code in 64-bit mode - linux

I have written a simple assembly code which I am trying to compile in 64-bit mode. Here is the code:
extern printf
section .rodata
readinfo db `%d\n`, 0
section .text
global main
main:
mov rbp, rsp ; for correct debugging
mov rax, 5
push rax
push readinfo
call printf
add rsp, 8
xor rax, rax
mov rsp, rbp
ret
And here are the instructions I give to nasm and gcc (as I have read on other posts, gcc automatically links the object file with the default c libraries):
nasm -f elf64 -o test.o test.asm -D UNIX
gcc -o test test.o
However, I get the following relocation error:
/usr/bin/x86_64-linux-gnu-ld: test.o: relocation R_X86_64_32S against
`.rodata' can not be used when making a PIE object; recompile with
-fPIC
/usr/bin/x86_64-linux-gnu-ld: final link failed: Nonrepresentable
section on output
collect2: error: ld returned 1 exit status
When I compile with the '-no-pic' option to disable positionally-independent code, it compiles without errors, but after execution I get a segfault with no output.
When I recompile the code in 32-bit (replacing 64-bit registers with 32-bit), I get no error. The commands are:
nasm -f elf32 -o test.o test.asm -D UNIX
gcc -o test test.o -m32
My question is: why can't I compile the code with PIC in 64bit mode?
PS:
This is not a duplicate of Can't link a shared library from an x86-64 object from assembly because of PIC , since the error is different and the solution found in that post has nothing in relation with my problem. I have edited the error output to specify.

The mistake was that I was using the wrong calling convention. In architecture x86_64 the first two arguments are passed in rdi and rsi, respectively, without using the stack. Also, I needed to add "wrt ..plt" to the call. The following code works:
extern printf
section .rodata
readinfo db `%d\n`, 0
section .text
global main
main:
mov rbp, rsp ; for correct debugging
mov rsi, 5
mov rdi, readinfo
xor rax, rax
call printf wrt ..plt
xor rax, rax
mov rsp, rbp
ret
The commands for nasm and gcc haven't changed.

Related

SYS_READ call only works once, seemingly skips over code when ran again

I'm trying to rewrite a little text game in assembly for fun, and I noticed that I'm only able to run the following code successfully once. If I run it again elsewhere, it will seemingly skip over the code.
I am compiling using the following command:
nasm -f elf64 -o test.o textgame.s && ld -o test test.o && ./test
Full code
mov rax, 0
mov rdi, 0
mov rsi, buffer
mov rdx, buffer_len
syscall
Solved! Thanks to Jester!
With a buffer_len of 1 there is no place for the linefeed so the next time around that will be read.

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

"relocation R_X86_64_32S against `.bss' can not be used when making a shared object”

I'm absolutely green in this but during classes, teacher gave us file he wrote just for us to run it and it worked fine then, but when I try to do it at home (I use Linux on VirtualBox) and use:
nasm -f elf64 hello.asm -o hello.o
gcc hello.o -o hello
I get an error "relocation R_X86_64_32S against `.bss' can not be used when making a shared object; recompile with -fPIC”. Can someone please explain what to do to make it work?
global main
extern printf
section .data
napis: db ' Hello world! - po raz %ld',10,0
liczba_iteracji: equ 5
section .bss
licznik: resb 1
section .text
main:
push rbp
mov rbp,rsp
mov byte [licznik],0
petla: ;naiwna!
inc byte [licznik]
mov rdi, qword napis
mov rsi, qword [licznik]
mov rax, 0
call printf
cmp byte [licznik],liczba_iteracji
jnz petla
mov rsp,rbp
pop rbp
mov rax,1 ;SYS_EXIT
mov rbx,0
int 80h
You need to make certain you're writing position independent code. The idea of PIC is that to make code truly position-independent, you need at least one level of indirection. That level of indirection is IP-relative addressing, and when that is not enough, you will need a second layer, the Global Offset Table or GOT.
In NASM, you will find the DEFAULT REL directive(s) useful.
I had the same issue. The reason GCC gives this error is because it assumes (version 6.3.0 here) you are building a shared object (when, clearly, you are not), therefore presence of .bss makes it crazy. So you can either fix this by passing -static option: gcc hello.o -static -o hello (worked in my case), or using Clang as a linker: clang hello.o -o hello. No complaints from the latter.

Segmentation Fault at the end of a simple _start that doesn't do anything

when I assembly the following assembly code I get the error Segmentation fault (core dumped)
section .text
global _start
_start:
mov eax, 8
My Makefile is as follows
all:
nasm -f elf64 -o asm.o asm.s
ld asm.o -o asm
rm asm.o
I don't know what the issue is.
I am running 64-bit Ubuntu.
The CPU execute the program, findd the mov eax, 8 instruction, executed it... and what now? There are no more instructions in the object file, but nobody told the CPU! It executes whatever is next, probably no valid instruction, which results in a segmentation fault, just like #MichaelPetch said.
The easiest solution IMO is to use a wrapper, which takes care of initializing and cleaning up your program, e.g., GCC. Just put the mov eax, 8 into the main function, which you may be familiar with from C.
Modify the source file as follows:
section .text
global main
main:
mov eax, 8
ret
(main is a function, so you need the ret instruction to return from it.)
and use the following script:
nasm -f elf64 -o asm.o asm.s
gcc asm.o -o asm
rm asm.o
I made this fast example.asm with 64 bits registers
section .data
msg db "Hello world"
section .text
global _start:
_start:
call _myfunk
call _exit
_myfunk:
mov rax,1
mov rdi,1
mov rdx,12
mov rsi,msg
syscall
ret
_exit:
mov rax, 60
mov rdi,0
syscall
To compile this assembly code you can use nasm and ld commands
nasm -f elf64 example.asm -o example.o
ld example.o -o example.elf
and now run the program ./example.elf
I am only started with assembly, but I can help - now it's 4 years old and you may not need it, but maybe others.
In this:
section .text
global _start
_start:
mov eax, 8
you forgot to stop the program after finishing the code,
so inside the _start label, you can add 3 lines, just like below:
(i don't know why you need 8 in eax reg. so i am moving it to ebx)
section .text
global _start
_start:
mov eax, 8
mov ebx, eax
mov eax, 1 ; this is for system call exit
int 0x80 ; system call
Here the value of ebx will be treated as return value , so you can get the value (8) by typing in your terminal
echo $?
good luck :)

Resources