Can't link assembly file in Mac OS X using ld - macos

I'm trying to run a basic assembly file using 64 Bit Mac OS X Lion, using nasm and ld which are installed by default with Xcode.
I've written an assembly file, which prints a character, and I got it to build using nasm.
nasm -f elf -o program.o main.asm
However, when I go to link it with ld, it fails with quite a few errors/warnings:
ld -o program program.o
ld: warning: -arch not specified
ld: warning: -macosx_version_min not specificed, assuming 10.7
ld: warning: ignoring file program.o, file was built for unsupported file format which is not the architecture being linked (x86_64)
ld: warning: symbol dyld_stub_binder not found, normally in libSystem.dylib
ld: entry point (start) undefined. Usually in crt1.o for inferred architecture x86_64
So, I tried to rectify a few of these issues, and got nowhere.
Here's one of things I've tried:
ld -arch i386 -e _start -o program program.o
Which I thought would work, but I was wrong.
How do you make the object file a compatible architecture that nasm and ld will agree with?
Also, how would you define the entry point in the program (right now I'm using global _start in .section text, which is above _start, which doesn't seem to do much good.)
I'm a bit confused as to how you would successfully link an object file to a binary file using ld, and I think I'm just missing some code (or argument to nasm or ld) that will make them agree.
Any help appreciated.

You need to use global start and start:, no underscore. Also, you should not be using elf as the arch. Here is a bash script I use to assemble my x86-64 NASM programs on Mac OS X:
#!/bin/bash
if [[ -n "$1" && -f "$1" ]]; then
filename="$1"
base="${filename%%.*}"
ext="${filename##*.}"
nasm -f macho64 -Ox "$filename" \
&& ld -macosx_version_min 10.7 "${base}.o" -o "$base"
fi
If you have a file called foo.s, this script will first run
nasm -f macho64 -Ox foo.s
Which will create foo.o. The -Ox flag makes NASM do some extra optimization with jumps (i.e. making them short, near or far) so that you don't have to do it yourself. I'm using x86-64, so my code is 64-bit, but it looks like you're trying to assemble 32-bit. In that case, you would use -f macho32. See nasm -hf for a list of valid output formats.
Now, the object file will be linked:
ld -macosx_version_min 10.7 foo.o -o foo
I've set the -macosx_version_min option to quiet NASM down and prevent a warning. You don't have to set it to Lion (10.7). This will create an executable called foo. With any luck, typing ./foo and hitting return should run your program.
In regard to the ld: warning: symbol dyld_stub_binder not found, normally in libSystem.dylib warning, I get that every time too and I'm not sure why, but everything seems fine when I run the executable.

OK, looking at your samples I assume you either used a generic nasm or linux assembly tutorial.
The first thing you need to take care of is the binary format created by nasm.
Your post states:
ld: warning: ignoring file program.o, file was built for unsupported file format which is not the architecture being linked (x86_64)
Thats the result of the '-f elf' parameter which tells nasm you want a 32bit ELF object (which would be the case for e.g. linux). But since you're on OSX what you want is a Mach-O object.
Try the following:
nasm -f macho64 -o program.o main.asm
gcc -o program program.o
Or if you wan't to create a 32bit binary:
nasm -f macho32 -o program.o main.asm
gcc -m32 -o program program.o
Regarding the _start symbol - if you wan't to create a simple program that will be able
to use the provided libc system functions then you shouldn't use _start at al.
It's the default entry point ld will look for and normaly it's provided in your libc / libsystem.
I suggest you try to replace the _start in your code by something like '_main'
and link it like the example above states.
A generic libc-based assembly template for nasm could look like this:
;---------------------------------------------------
.section text
;---------------------------------------------------
use32 ; use64 if you create 64bit code
global _main ; export the symbol so ld can find it
_main:
push ebp
mov ebp, esp ; create a basic stack frame
[your code here]
pop ebp ; restore original stack
mov eax, 0 ; store the return code for main in eax
ret ; exit the program
In addition to this I should mention that any call's you do on OSX need to use an aligned stack frame or your code will just crash.
There are some good tutorials on that out there too - try searching for OSX assembly guide.

It's probably easier just to let gcc do the heavy lifting for you, rather than trying to drive ld directly, e.g.
$ gcc -m32 program.o -o program

The mac gcc compiler won't link elf objects. You need a cross compiler...
http://crossgcc.rts-software.org/doku.php?id=compiling_for_linux
Then you can proceed with something similar to this...
/usr/local/gcc-4.8.1-for-linux32/bin/i586-pc-linux-ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o

Related

nasm - Can't link object file with ld on macOS Mojave

I'm trying to assemble a simple Hello World, which worked fine in the previous macOS version:
global start
section .text
start: mov rax, 0x02000004
mov rdi, 1
mov rsi, msg
mov rdx, 13
syscall
mov rax, 0x02000001
xor rdi, rdi
syscall
section .data
msg: db "Hello world!", 10
Then I use nasm and ld as I did before:
$ nasm -f macho64 hello.asm
$ ld hello.o -o hello
But ld gives me the following error:
ld: warning: No version-min specified on command line
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for inferred architecture x86_64
I tried switching start to _main, but got the following:
ld: warning: No version-min specified on command line
ld: dynamic main executables must link with libSystem.dylib for inferred architecture x86_64
Don't even know what that might mean.
ld needs -lSystem flag to prevent it from throwing this error. Also it needs -macosx_version_min to remove the warning. The correct way of using ld would be: ld hello.o -o hello -macosx_version_min 10.13 -lSystem.
Updated on macOS 11 and above, you need to pass -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib as well so that it locates the -lSystem library correctly. You can use -L$(xcode-select -p)/SDKs/MacOSX.sdk/usr/lib to evaluate the right path dynamically if required.
In addition to the #Verloren answer above (https://stackoverflow.com/a/52830915/1189569)
I had an issue with macOS Big Sur (macOS 11.1), where flag -lSystem could not locate libSystem.dylib, with the error
ld: library not found for -lSystem
I found out for macOS Big Sur, quoted from the link:
https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes
New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic
linker cache of all system-provided libraries. As part of this change,
copies of dynamic libraries are no longer present on the filesystem.
Code that attempts to check for dynamic library presence by looking
for a file at a path or enumerating a directory will fail...
that all copies of dynamic libraries are not located in usr/lib/ and similar, so flag -lSystem could not found libSystem.dylib by default.
The solution to this was to update/install the latest version of Command Line Tools, if not already, and to set flag -L of the ld command to /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib.
So full command would look like this:
ld hello.o -o hello -macosx_version_min 11.0 -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lSystem
Simpler answer. ld is defaulting to dynamic linking and tries to load crt1 which is looking for main. So specify static linking.
% ld -e start -static hello.o -o hello
% ./hello
Hello world!
In macOS 11.2 i used:
ld hello.o -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib -lSystem

Error calling standard C library functions from NASM?

I'm quite new to assembly programming. I use NASM 2.11.05 on a Windows 7 (64-bit) platform to run some sample code. The problem arises when I try to call standard C functions from my assembly code. This is my assembly source:
global main
extern puts
section .text
main:
push message
call puts
ret
message:
db "Hola, mundo", 0
When I compile with NASM, I use this command line: nasm -fwin32 file.asm
which produces file.obj. Now, when I try to link it with ld or gcc, I keep getting errors. Some things I tried:
gcc -m32 -nostartfiles file.obj (gives the error that i386:x86-64 architecture of input file is not compatible with i386 output).
ld file.obj (gives the error undefined reference to puts).
Can anyone please guide me on how to resolve this?
In the end, one line at the top of my ASM file settled it. This is that line.
[BITS 32]
However, the output file still keeps crashing: anyone who can explain that is welcome!
You can just compile in a different way, like:
Create an object of your .asm file with:
GCC: nasm -f elf file.asmor
LD: ld -m elf_i386 file.o -o file
Link object file created with gcc -m32 -o file file.o
Run with ./file

are there debugging options for ld

I have written an assembly program that, for testing purposes, just exits. The code is as follows:
section .text
_global start
_start:
mov eax, 1
mov ebx, 0
int 0x80
The program is obviously in 32-bit; however, I am using 1 64-bit processor and operating system, so I compiled it (using nasm) and linked it as follows:
nasm -f elf exit.asm
ld -m elf_i386 -s -o exit exit.o
debugging the program with gdb, I can't list the code since there are no debugging symbols.
(gdb) list
No symbol table is loaded. Use the "file" command.
In using gcc, you can use the options -ggdb to load the symbols while compiling a c file. but since I don't how to use gcc to compile 32-bit assembly for 64-bit machines (I have searched this but can't find a solution,) I am forced to use ld. can I load the debugging symbols using ld? sorry for the long question and the excess information. Thanks in advance.
Debugging information is generated by nasm when you pass -g. Additionally, you also need to specify what type of debugging information you want (typically dwarf), which is done with the -F switch. So to assemble your file, write
nasm -f elf -F dwarf -g file.asm
then link without -s to preserve the symbol table and debugging information:
ld -m elf_i386 -o file file.o
The -s switch tells ld to "strip" the debugging info. Lose that!

ld MinGW link to standard C library

I have a problem with following code.
extern printf
global _main
main:
push msg
call printf
ret
msg db "Hello world",0
I assemble this with NASM using nasm -fwin32 test.asm Then i link it using ld test.obj.
And it tells me "test.obj:test.asm:(text+0x6): undefined reference to 'printf'"
How to link my file to standard C libraries? I have ld from latest MinGW.
To assemble code :
nasm -fwin32 test.asm
Microsoft will prefix functions using the cdecl calling convention with a underscore.
To be match to the C calling convention printf should be _printf.
The same applies for _main instead of main.
And link with:
ld test.obj -lmsvcrt -entry=_main -subsystem=console -o test.exe
Here -entry command line option is used to invoking ld to specify the entry point for program .
Then use -l options to pass msvcrt library to the ld linker, otherwise you will get an error message, (undefined reference to `printf') which means that the linker did not found the symbol printf in the specified object file produced by NASM.
Here is completed source:
global _main
extern _printf
section .text
_main:
push msg
call _printf
add esp, 4 ;adjust the stack
ret
msg db "Hello world",0
I can see several issues with your code. First, you've got an underscore on global _main but not on main:. These should match. You can either use underscores throughout, or - what I would do - not at all... and for Windows, assemble as nasm -f win32 --prefix _ test.asm. This would make it "portable" in that, for Linux, it would assemble, without the --prefix _ without the underscores. Linux doesn't use underscores on global or extern symbols. If, by some chance, you were using OpenWatcom C, you could use --postfix _. Yeah, OpenWatcom uses trailing underscores. Yeah, I know they told us C was standardized. but once you get under the hood, this isn't really true.
The other big issue is that after calling _printf, you need to add esp, 4 (or pop a dummy register) to "clean up the stack". If you're using Windows APIs, they use the STDCALL calling convention in which "callee cleans up", so you don't want to do this. Mixing C calls (CDECL calling convention) and Windows APIs might get confusing, but should work.
I think Carl has the right idea with using gcc to link it. There's nothing to "compile", but gcc knows the proper command line to ld. gcc -o test.exe test.obj will probably be enough (maybe add -m32 if the latest MinGW expects to be doing 64-bit code). This will link in some "startup code" which calls _main. This will increase the size of your executable slightly, and you "might" be able to get along without it, but it's easier to just do it.
In Linux, we can use ld directly (the command line is horrid), but ld is looking for _start, not main, as the entrypoint. We can tell ld -e main, but this entrypoint is not called(!) and there's no possible way to ret from it! The situation is probably different in Windows. You would need - as a bare minimum - -lc to tell ld that we want those C libraries. Easiest to "let gcc do it" - it won't touch your .asm code (but does link in that "startup code"). Happy Hello World! :)
Use a compiler front-end to link:
cc test.obj
If you really want to use ld directly (and you shouldn't), use the -v flag to cc to figure out what complete command line you need. For example, on my machine, it's:
ld -demangle -dynamic -arch x86_64 -macosx_version_min 10.8.0 \
-o a.out test.obj -lSystem \
/usr/bin/../lib/clang/4.2/lib/darwin/libclang_rt.osx.a
If I use GCC instead of Clang, it's even crazier:
ld -dynamic -arch x86_64 -macosx_version_min 10.8.4 -weak_reference_mismatches \
non-weak -o a.out -lcrt1.10.6.o \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1/x86_64 \
-L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/x86_64 \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1 \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc \
-L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1 \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1/../../.. \
-L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/../../.. \
test.obj -lgcc -lSystem

Details on gdb memory access complaint

I have an object file compiled using as (from assembler code).
If I link it using ld, when I try to stepi (or nexti) gdb complains about memory access at address 0x0. If I link it using gcc, all is fine.
I am guessing the problem is caused by ld, which produces fewer sections when compared to the linking result of gcc.
Is there a way to configure gdb to be more verbose so I can maybe figure out what's wrong with the executable?
(gdb) b main
Breakpoint 1 at 0x100000f8e
(gdb) r
Breakpoint 1, 0x0000000100000f8e in main ()
(gdb) x/10i $pc
0x100000f8e <main>: fbld 0x6c(%rip) # 0x100001000 <data1>
0x100000f94 <main+6>: fimul 0x7a(%rip) # 0x100001014 <data2>
0x100000f9a <main+12>: fbstp 0x60(%rip) # 0x100001000 <data1>
0x100000fa0 <main+18>: mov0x0 $0x2000001,%rax
0x100000fa7 <main+25>: mov $,%rdi
0x100000fae <main+32>: syscall
(gdb) si
Cannot access memory at address 0x0
0x0000000100000f94 in main ()
PS: The executable itself runs as expected in both versions.
Later edit: commands i've used to compile:
as -arch x86_64 src.s -o src.o
ld -e _main -arch x86_64 src.o -o src
gcc -o src src.o
gdb has a "show debug" command, giving various internal debug settings. E.g. "set debug target 1" will turn on tracing for gdb's interaction with the target process. You might want to experiment with every flag they have (there aren't that many).
GCC doesn't actually do the linking, it just calls ld on your behalf. The options it's providing must be different from the ones you are using.
Per this thread:
How to get GCC linker command?
You should be able to see the ld invocation's command line by running gcc -v.
That should tell you how to modify your ld command line so things work for you.

Resources