MinGW Win32 + nasm: "undefined reference" - gcc

I am currently developing an OS for learning purposes, and it's been working fine until now. Then I tried to call an assembler function, compiled with nasm, -fwin32, from C code, but all I got was an "undefined reference" error. I have created a small example in pure assembler, which has the same problem, but is easily understandable and way smaller:
It includes two files:
test.asm:
[bits 32]
global _testfunc
_testfunc:
ret
test2.asm:
[bits 32]
extern _testfunc
global _testfunc2
_testfunc2:
call _testfunc
ret
Here is my compiler / linker script (using windows batch files):
nasm.exe -f win32 test.asm -o test.o
nasm.exe -f win32 test2.asm -o test2.o
ld test.o test2.o -o output.tmp
This results in the error:
test2.o:test2.asm:(.text+0x1): undefined reference to `testfunc'
To extend the question, the same happens when the function is called from C:
test.c:
extern void testfunc(void);
void start()
{
testfunc();
}
With this linker script:
gcc -ffreestanding -c test.c -o testc.o
nasm.exe -f win32 test.asm -o test.o
ld test.o testc.o -o output.tmp
In test.o, test2.o and testc.o, it always says _testfunc, so the error has nothing to do with leading underscores!

In my MinGW setup you need a section directive before the code.
; foo.asm
[bits 32]
global _testfunc
section .text
_testfunc:
ret
Then assemble to win32 format:
nasm -fwin32 foo.asm -o foo.o
Now you can check that testfunc is there:
$ nm foo.o
00000000 a .absolut
00000000 t .text
00000001 a #feat.00
00000000 T _testfunc
The T means text section global, so we're good to go.
Note I'd avoid naming anything test since this is a shell command. This can cause endless grief.
The C function is as you showed it, but name the file something else:
// main.c
extern void testfunc(void);
int main(void)
{
testfunc();
return 0;
}
Then to build an executable let gcc do the heavy lifting because ld sometimes needs arcane arguments.
gcc -ffreestanding main.c foo.o -o main

Your missing something important, your code is not in a code section!
Your asm files should look like the following:
test.asm
global _testfunc
section .text ; <<<< This is important!!!
; all code goes below this!
_testfunc:
ret
test2.asm
extern _testfunc
global _testfunc2
section .text ; <<<< Again, this is important!!!
_testfunc2:
call _testfunc
ret

Related

what are the correct flags to mix assembly and C without losing .data constants

I am working on a utility in assembler (x86, NASM 2.13, gcc 7.50, Ubuntu 18.X) to be called from C. The problem that I am running into is that the string constant addresses seem to be lost in the making process, so messages are not printing. I can see the string constants in the final binary.
This is the sample assembler code
SECTION .data
error_len_message: db "test", 10
error_len_message_len: equ $-error_len_message
SECTION .text
global test_func
test_func:
mov eax, 4
mov ebx, 1
mov ecx, error_len_message
mov edx, error_len_message_len
int 80H
The header file would be trivial as it would only need the signature used in main.c
#include "....."
int main(void) {
test_func();
return 0;
}
The make file of the executable is as follows:
util_tests: ../asm/utilities/utilities.o
gcc -Wall -o ./build/$# main.c $?
../asm/utilities/utilities.o:
cd ../asm/utilities && make && cd ../../util_tests;
clean:
cd ../asm/utilities && make clean && cd ../../util_tests;
The make file of the utilities is as follows:
utilities.o: test/test.o
ld $? -o $#
test/test.o:
cd test && make && cd ..;
clean:
cd test && make clean && cd ..;
rm *.o
The make file of test assembler code is:
test.o: test.asm
nasm -f elf64 -g -F dwarf $?
clean:
rm *.o
I have no trouble turning this into an executable using assembler only and printing strings defined in .data section. Make file:
test: test.o
ld -o test $^
test.o: *.asm
nasm -f elf64 -g -F stabs $?
clean:
rm *.o
I can successfully define and use variables in the .bss section (not shown for brevity)
The problem, I believe, is in the way I am building the utility. Thanks in advance!
So after adding the pertinent "default rel" to the assembler source file, removing direct calls to int 80H, and adding calls printf, the linker fails because it also tries to relocate "printf#glibc_x.x.x". After trying linker options to ignore unresolved symbols, it seems like a dead end.
So it seems there are only two choices, assemble in 32 bit to avoid problems with "int 80H", or return status/results and let the caller deal with the rest. The following assemble, link, and gdb displays labels and offsets matching correctly
DEFAULT REL
SECTION .text
global silly
silly:
lea r9, [message]
ret
SECTION .data
message: db "A Message", 10
Makefile:
silly.so: silly.o
ld -shared $? -o $#
silly.o: silly.asm
nasm -f elf64 -g -F dwarf $?
clean:
rm *.o
rm *.so
Hopefully, this would be useful to anyone

How to solve this linker error I get when trying to link C++ with Assembly?

I'm trying to link a C++ file and an Assembly file. The Assembly file defines a C++ function called add. I'm using a 64 bit Mac OS. I get the following error when I try to compile the program:
Undefined symbols for architecture x86_64:
"_add", referenced from:
_main in main-d71cab.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [main.o] Error 1
Makefile
hello: main.o hello.o
g++ main.o hello.o -o hello
hello.o: hello.asm
nasm -f macho64 hello.asm -o hello.o
main.o: main.cpp
g++ main.cpp -o main.o
hello.asm
global add
segment .text
add:
mov rax, rdi
add rax, rsi
ret
main.cpp
#include <iostream>
extern "C" int add(int a, int b);
int main(int argc, char* argv[]) {
int a, b;
std::cout << "Enter two numbers\n";
std::cin >> a;
std::cin >> b;
std::cout << "Sum of a and b is " << add(a, b) << std::endl;
return 0;
}
I'd really appreciate any help. Thanks!
On OS/X, the C calling convention is that functions must have an underscore on them (unless it is overridden) when exported from an object in order to be visible to C. You are saying that add is using C calling convention when you put extern "C" on this prototype:
extern "C" int add(int a, int b);
This is in fact correct, but in order for your assembler code to conform you also need to make sure your assembler functions that are going to be global and visible to C/C++ code have a leading underscore on them. Your assembler code would start like this:
global _add
segment .text
_add:
The other problem you have is in your Makefile and the way you generate main.o from main.cpp.
main.o: main.cpp
g++ main.cpp -o main.o
This tells g++ to compile main.cpp to an executable called main.o . What you really want to do is tell g++ to skip the linking to an executable part, and that the output file main.o will actually be an object. To do that change the line to be:
main.o: main.cpp
g++ -c main.cpp -o main.o
The -c option says to skip the linking stage (that generates an executable and simply output an object that will be linked later).
Without this change when it tries to make main.o, it thinks you want an executable, and can't find the function _add because it is isn't in a file it knows about. It is in hello.o but the command g++ main.cpp -o main.o doesn't know that.

linux linking assembly with gcc gives many errors

I am trying to compile and link a simple "hello, world!" program with GCC. This program uses the "printf" C function. The problem that I am having is that the terminal throws back multiple errors. I am running Archlinux, compiling with NASM, linking with GCC. Here is my code:
; ----------------------------------------------------------------------------
; helloworld.asm
;
; Compile: nasm -f elf32 helloworld.asm
; Link: gcc helloworld.o
; ----------------------------------------------------------------------------
SECTION .data
message db "Hello, World",0
SECTION .text
global main
extern printf
section .text
_main:
push message
call printf
add esp, 4
ret
The errors that I receive are as follows:
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/libgcc.a when searching for -lgcc
/usr/bin/ld: cannot find -lgcc
collect2: error: ld returned 1 exit status
Can someone tell me what is causing these errors and what I need to do to fix them?
Thanks in advance,
RileyH
For such things, you should first understand what exactly gcc is doing. So use
gcc -v helloworld.o -o helloworld
and what is happenning is that you have a 64 bits Linux and linking a 32 bits object in it. So try with
gcc -m32 -v helloworld.o -o helloworld
But I think that you should avoid coding assembly today (optimizing compilers do a better work than you can reasonably do). If you absolutely need a few assembly instructions, put some asm in your C code.
BTW, you could compile with gcc -fverbose-asm -O -wall -S helloworld.c and look inside the generated helloworld.s; and you could also pass .s files to gcc

NASM Hello World either segfaults or bus errors in Mac OS X

I'm writing Hello World in NASM, and I can get it to echo Hello World to the console, but the program segfaults if I don't run it with Make.
Trace with Makefile:
$ make
nasm -f macho -o hello.o --prefix _ hello.asm
ld -o hello hello.o -arch i386 -lc -macosx_version_min 10.6 -e _start -no_pie
./hello
Hello World!
Trace with manual commands:
$ nasm -f macho -o hello.o --prefix _ hello.asm
$ ld -o hello hello.o -arch i386 -lc -macosx_version_min 10.6 -e _start -no_pie
$ ./hello
Segmentation fault: 11
hello.asm:
[bits 32]
section .data
msg: db "Hello World!", 0
section .text
global start
extern puts
extern exit
start:
push msg
call puts
add esp, 4
push 0
call exit
Makefile:
# Linux defaults
FORMAT=-f elf
MINV=
ARCH=-arch i386
LIBS=
RUN=./
EXECUTABLE=hello
PREFIX=
ENTRY=
PIE=
# Windows
ifeq (${MSYSTEM},MINGW32)
FORMAT=-f win32
EXECUTABLE=hello.exe
PREFIX=--prefix _
ENTRY=-e _start
ARCH=
LIBS=c:/strawberry/c/i686-w64-mingw32/lib/crt2.o -Lc:/strawberry/c/i686-w64-mingw32/lib -lmingw32 -lmingwex -lmsvcrt -lkernel32
ENTRY=
RUN=
endif
# Mac OS X
ifeq ($(shell uname -s),Darwin)
FORMAT=-f macho
PREFIX=--prefix _
ENTRY=-e _start
LIBS=-lc
MINV=-macosx_version_min 10.6
PIE=-no_pie
endif
all: test
test: $(EXECUTABLE)
$(RUN)$(EXECUTABLE)
$(EXECUTABLE): hello.o
ld -o $(EXECUTABLE) hello.o $(ARCH) $(LIBS) $(MINV) $(ENTRY) $(PIE)
hello.o: hello.asm
nasm $(FORMAT) -o hello.o $(PREFIX) hello.asm
clean:
-rm $(EXECUTABLE)
-rm hello.o
Specs:
ld 64-134.9
LLVM 3.1svn
NASM 0.98.40
Make 3.81
Xcode 4.5
Mac OS X 10.8.1
MacBook Pro 2009
2 things, your hello world string is not NULL terminated and as I mentioned in another post, when you use C functions, you MUST adjust esp after each call
You tore down your stack frame twice:
mov esp, ebp
pop ebp
...
leave
You only need one of those, since leave is equivalent to mov esp, ebp; pop ebp.
See http://michaux.ca/articles/assembly-hello-world-for-os-x for several example hello world programs. Note that all of them exit the program explicitly with
; 2a prepare the argument for the sys call to exit
push dword 0 ; exit status returned to the operating system
; 2b make the call to sys call to exit
mov eax, 0x1 ; system call number for exit
sub esp, 4 ; OS X (and BSD) system calls needs "extra space" on stack
int 0x80 ; make the system call
because you cannot ret from an entry point (there's nothing to return to).
Also note that if you call the function main and don't supply the e option to ld, then libc's entry point will be called. In that case, it is permissible to ret since you will return control to libc (which calls exit on your behalf).

link nasm program for mac os x

i have some problems with linking nasm program for macos:
GLOBAL _start
SEGMENT .text
_start:
mov ax, 5
mov bx, ax
mov [a], ebx
SEGMENT .data
a DW 0
t2 DW 0
fry$ nasm -f elf test.asm
fry$ ld -o test test.o -arch i386
ld: warning: in test.o, file was built for unsupported file format which is not the architecture being linked (i386)
ld: could not find entry point "start" (perhaps missing crt1.
fry$ nasm -f macho test.asm
fry$ ld -o test test.o -arch i386
ld: could not find entry point "start" (perhaps missing crt1.o)
can anyone help me?
The Mac OS X linker can't link ELF objects. It works only with the Mach-O executable format. Unless you want to figure out how to translate the object files, you'll probably be better off writing code that works with the Mac OS X assembler.
Edit: As #Fry mentions in the comment below, you can make nasm put out Mach-O objects. In that case, the problem is simple - take the _ off of _start in both places in your source file. The result links fine.
nasm -f macho test.asm
ld -e _start -o test test.o
For people who need to stick with the elf format and develop on a mac, 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

Resources