I'm following a assembly book which uses the yasm assembler and ld linker. I'm on OSX 10.12 and I'm trying to assembly to Mach-O format. Unfortunately, I'm receiving a segmentation fault. This is the original .asm file:
BITS 64
segment .data
a dd 4
segment .bss
g resd 1
segment .text
global start
start:
push rbp
mov rbp, rsp
sub rsp, 16
xor eax, eax
leave
ret
I compile it:
yasm -f macho64 -m amd64 -l memory.lst -o memory.o memory.asm
link it:
ld memory.o -o memory
and run it in lldb, I receive this error:
thread #1: tid = 0xb3b4b, 0x0000000000000001, stop reason = EXC_BAD_ACCESS (code=1, address=0x1)
frame #0: 0x0000000000000001
error: error reading data from section __PAGEZERO
In lldb, I ran 'target modules dump sections', and I see that it's __PAGEZERO segment is defined as so:
[0x0000000000000000-0x0000000000001000) --- memory.__PAGEZERO
I looked at a normal Mach-O binary built with clang, and the __PAGEZERO segment looks like this:
[0x0000000000000000-0x0000000100000000) --- test.__PAGEZERO
I then noticed that it's actually the linker that creates the PAGEZERO segment. I believe clang uses a special linker called 'lld'. My question is:
Is my error actually caused by reading from PAGEZERO.
If so, can I tell my linker (ld) to define PAGEZERO in the correct size?
SOLVED: I changed the link command to:
ld memory.o -macosx_version_min 10.12 -lSystem -o memory
This doesn't change the PAGEZERO size, so I'm not sure how it fixed it, but it works now.
Related
I have the following assembly code that i got from a x86 assembly tutorial online:
section .text
global _start ;must be declared for linker (ld)
start: ;tells linker entry point
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db 'Hello, world!', 0xa ;string to be printed
len equ $ - msg ;length of the string
I saved the above code in a file "hello.asm"
now when i compile and link it in my terminal, i get the following error!
root#mac:~# nasm -f macho hello.asm && gcc -o hello hello.o
ld: warning: ignoring file hello.o, file was built for i386 which is not the architecture being linked (x86_64): hello.o
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The code you have is valid Linux code. However, it is not valid MacOS code. This is because the system interrupt int 0x80 is meant to call the Linux system kernel not the MacOS one. If you have a older version of MacOS that is Linux based this might still work.
The other problem is that this code is 32-bit and you are compiling it like 64-bit code. To solve this you should add -m32 to the gcc command.
This question already has an answer here:
32-bit absolute addresses no longer allowed in x86-64 Linux?
(1 answer)
Closed 4 years ago.
I have this simple program to compute the square root of a floating point number
global main
extern printf
section .data
float_t db '%f',0x0
val dq 123.45
res dq 0x0
section .text
main:
fld qword[val]
fsqrt
fst qword[res]
xor rax,rax
mov rdi, float_t
mov rsi, [res]
call printf
mov rax,60
mov rdi,0
syscall
I assembled it by
$ nasm -f elf64 fpu.asm -o fpu.o
and then tried to link to glibc with gcc as
$ gcc fpu.o -o fpu
GCC complains with:
/usr/bin/ld: fpu.o: relocation R_X86_64_32S against `.data' can not be used
when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
As stated by Michael Petch in the comments above,
I would expect building with -static may work
I had the same problem and this fixed it.
I found the following code from http://www.dreamincode.net/forums/topic/328714-my-program-keeps-crashing/.
global start
;~ msvcrt.dll
extern _printf
%define printf _printf
;~ kernel32.dll
extern ExitProcess, GetCommandLineW, LocalFree
%define GetCommandLine GetCommandLineW
;~ shell32.dll
extern CommandLineToArgvW
%define CommandLineToArgv CommandLineToArgvW
SECTION .data
message db 'Hello, World', 13, 10, 0
fmtstr db "%s", 0
fmtstrCL db "Arg","%d", " = ", "%S", 13, 10, 0
section .bss
pNumArgs resd 1
section .text
start:
call GetCommandLine
push pNumArgs
push eax
call CommandLineToArgv
mov esi, eax
mov ebx, [pNumArgs]
DisplayArgs:
dec ebx
push dword[esi + 4 * ebx]
inc ebx
push ebx
push fmtstrCL
call printf
add esp, 4 * 3
dec ebx
jnz DisplayArgs
push esi
call LocalFree
push message ; Push address of "Hello, world!" onto the stack
push fmtstr ; push address of formatter onto the stack
call printf ; Print the message
add esp, 4 * 2 ; adjust stack pointer
push 0
call ExitProcess
My goal is to learn assembly language by reading other people's code and eventually write my own. I cannot figure out how to link 32-bit assembly programs on my 64-bit windows computer.
To assemble the program I use the command:
nasm -f win32 hello32.asm -o hello32.o
To link the object file I use:
gcc hello32.o -o hello32.exe
After I issue the link command I get the following error:
C:/Program Files/mingw-w64/x86_64-5.2.0-posix-seh-rt_v4-rev0/mingw64/bin/../lib/
gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: i386 arc
hitecture of input file `hello32.o' is incompatible with i386:x86-64 output
hello32.o:hello32.asm:(.text+0x24): undefined reference to `_printf'
hello32.o:hello32.asm:(.text+0x3f): undefined reference to `_printf'
C:/Program Files/mingw-w64/x86_64-5.2.0-posix-seh-rt_v4-rev0/mingw64/bin/../lib/
gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw3
2.a(lib64_libmingw32_a-crt0_c.o):crt0_c.c:(.text.startup+0x2e): undefined refere
nce to `WinMain'
collect2.exe: error: ld returned 1 exit status
I am using 64-bit mingw binaries that are supposed to be compatible with making 32-bit programs. I have tried switching to 32-bit mingw binaries and I get a massive amount of undefined reference errors. I can link simple skeleton files without any problems using the above commands. I have no idea what I am doing wrong and I would appreciate any guidance someone could give me.
i386 architecture of input file `hello32.o' is incompatible with i386:x86-64 output
NASM has created a 32 bit object file, but you are trying to link a 64 bit executable. You could try to use the -m32 switch to create a 32 bit executable, but you already found out that this causes another bunch of errors. I do not have a solution for that either.
To link your executable, use a 32 bit MingW environment. I tried MinGW4.6.2 32 bit which worked well.
Alternatively, you can use the linker (link.exe) from a Microsoft Visual Studio installation.
https://github.com/afester/CodeSamples/tree/master/Asm/nasm_win32 shows a hello world example together with a Makefile which uses the Visual Studio linker. Alternatively, using gcc helloworld.obj -o hello32.exe from a MingW32 installation works also.
Two issues:
You're using the option -f win32 but asking for the object file in *.o extension. The two formats, .o and .obj are not compatible. But of course, you're free to specify your own extension, and so nasm will obediently assemble your code into a file with i386 arc format .o file.
Next, you're asking gcc to build that hello32.exe, using the file hello32.o. Effectively, you gave gcc an arc format .o file, and asked to build a 64-bit PE format executable out of it. And then (naturally) gcc complains:
i386 architecture of input file `hello32.o' is incompatible with i386:x86-64 output
which is correct.
Two ways you can fix this:
Assemble with: nasm -fwin32 hello32.asm and then, link with gcc -m32 hello32.obj -o hello32.exe
Assemble with: nasm -fobj hello32.asm and then link with alink -subsys console -oPE hello32.o. You can get alink from here.
Let me know which worked for you.
P.S. I have outlined the problems I have faced myself in this blog, hope that helps.
I am trying to learn x86 assembly on Windows. I can assemble and link a program successfully using:
nasm -f win32 -g sandbox.asm -l sandbox.lst
ld -mi386pe -o sandbox.exe sandbox.obj
But when I try running my program with gdb, it says that it can't find the debug symbols. Nasm says that only the "null" debug symbol format is available for win32. Does this mean that it can't generate debug symbols for windows? If so, then how can I debug my program on windows?
Here is the example program that I am using:
section .data
section .bss
section .text
global start
start:
mov ebp, esp
mov eax, 0
ret
You must specify the debugging format to include in your binary (sometimes there is not one by default)
try -gcv8 instead of -g
Starting to learn assembly, I was given some Hello World assembly code created during the class on Linux. I would like to get it to work for 64-bit Mac OS X.
code.asm:
SECTION .data
hola: db "Hola!",10
tam: equ $-hola
SECTION .text
global main
main:
mov edx,tam
mov ecx,hola
mov ebx,1
mov eax,4
int 0x80
mov ebx,0
mov eax,1
int 0x80
This is what I do:
nasm -f macho32 -o object.o code.asm
gcc -m32 -o program object.o
Which tells me:
Undefined symbols for architecture i386: "_main", referenced from:
start in crt1.10.6.o ld: symbol(s) not found for architecture i386
Searching for this error, I found this question: nasm and gcc: 32 bit linking failed (64 bit Mac OS X)
One answer says
The problem you're having is that you're creating a 32-bit Linux(ELF)
object file which isn't compatible with the Mac OS X object format.
Try switching '-f elf' to '-f macho32'.
But I'm definitely using -f macho32. So what would the problem be then?
I've been trying to teach myself some entry-level Assembly programming too, and I ran into similar issues. I had originally compiled using nasm with elf, but that didn't work when I tried to use ld to link the object file and create the executable.
I think the answer you main question "what would the problem be then?" [to get this to run on 64bit MacOSX] is: You are using -f macho32 but expecting it to run on a 64bit machine, you need to change the command option to be -f macho64. Of course, this will not resolve the fact that your assembly code is written for a different architecture (more on that in a bit).
I found this handy answer on the right command to use in this instance to compile and link your code (after you refactor your assembly code to use the proper syntax instead of *nix as duskwuff stated): nasm -f macho64 main.asm -o main.o && ld -e _main -macosx_version_min 10.8 -arch x86_64 main.o -lSystem
After some searching, here's what I learned...
On Mac 64bit, it might be better to use the as assembler instead of nasm (if you want something more native), but if you want more portable code (learn the differences).
nasm doesn't come with the macho64 output type installed by default
Assembly is a pain in the keister (this aside)
Now that my learning rant is out of the way...
Here is the code which should operate on MacOSX 64 using nasm (if you have updated nasm with macho64, credit to Dustin Schultz):
section .data
hello_world db "Hello World!", 0x0a
section .text
global start
start:
mov rax, 0x2000004 ; System call write = 4
mov rdi, 1 ; Write to standard out = 1
mov rsi, hello_world ; The address of hello_world string
mov rdx, 14 ; The size to write
syscall ; Invoke the kernel
mov rax, 0x2000001 ; System call number for exit = 1
mov rdi, 0 ; Exit success = 0
syscall ; Invoke the kernel
Working code I used with the as assembler native to MacOSX64:
.section __TEXT,__text
.global start
start:
movl $0x2000004, %eax # Preparing syscall 4
movl $1, %edi # stdout file descriptor = 1
movq str#GOTPCREL(%rip), %rsi # The string to print
movq $100, %rdx # The size of the value to print
syscall
movl $0, %ebx
movl $0x2000001, %eax # exit 0
syscall
.section __DATA,__data
str:
.asciz "Hello World!\n"
Compile command: as -arch x86_64 -o hello_as_64.o hello_as_64.asm
Link Command: ld -o hello_as_64 hello_as_64.o
Execute Command: ./hello_as_64
Some helpful resources I found along my journey:
AS OSX Assembler Reference: https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/Assembler/Assembler.pdf
Writing 64 Bit Assembly on Mac OSX: http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/
Couldn't link object file using ld:
Can't link object file using ld - Mac OS X
OSX i386 SysCalls: http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/mach/i386/syscall_sw.h
OSX Master System Call Definitions: http://www.opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master
OSX Syscall: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/syscall.2.html
You would need to:
Change the label name from main to _main (in both places). Symbol naming works a little bit differently under Mac OS X.
Change the way you pass arguments to the system call. Mac OS X uses a different calling convention for the kernel from Linux; this code is not portable! I don't know as there's any official documentation for how it does work, but looking at the disassembly in GDB for a standard library function like _exit() may be instructive.
Here's _exit on my system, for instance:
<_exit+0>: mov $0x40001,%eax
<_exit+5>: call 0x96f124c2 <_sysenter_trap>
<_exit+10>: jae 0x96f10086 <_exit+26>
<_exit+12>: call 0x96f1007d <_exit+17>
<_exit+17>: pop %edx
<_exit+18>: mov 0x15a3bf9f(%edx),%edx
<_exit+24>: jmp *%edx
<_exit+26>: ret
<_exit+27>: nop
The extra bit set in 0x40001 is... weird, but can be safely ignored here.
The stuff following the call to _sysenter_trap is for error handling.
_sysenter_trap is:
<_sysenter_trap+0>: pop %edx
<_sysenter_trap+1>: mov %esp,%ecx
<_sysenter_trap+3>: sysenter
<_sysenter_trap+5>: nop
All things considered, you're probably better off linking to libSystem (the OS X equivalent of libc) instead of trying to call the kernel directly.
I've wrote a blog post on this topic: https://cs-flex.hashnode.dev/linux-assembly-on-macos
You have 3 main options:
VM -- i don't recommend
Renting a Linux server, not a bad option if you don't mind paying ~20-30$ a month
(My personal best option) using Docker to create a Linux container, that shares a folder (volume) and run assembler there. If you didn't use Docker before -- i still think this option is the best one.
You can read details in my blog post (especially if you didn't use Docker before). But in short, all you will need is this two files:
# Dockerfile
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y gcc
RUN apt-get install -y make
# docker-compose.yml
version: "3"
services:
linux:
image: linux-image
container_name: linux-container
build:
context: .
command: sleep 1000
volumes:
- .:/code
You will be able to run container and connect to it via
docker-compose up # build and run docker container
docker exec -it linux-container bash # "ssh" into container
after this you all your code in the folder with docker files will be "linked" to the folder /code/ inside `Docker. Therefore you can execute it inside docker container as if you were running Linux.