Linking error [undefined reference to `asm_main()'] in linking Assembly to C

170 Views Asked by At

My exact issue is that am trying to get the linked final exe from 3 files namely:

  • The main c loader
  • The main asm file
  • The accessory asm file that holds the functions.

I manage to convert them all successfully to their subsequent object files, but when linking, I get the undefined reference to 'asm_main()' error.

My C file is simple and looks like this:

#include "cdecl.h"

int PRE_CDECL asm_main( void ) POST_CDECL;
int main()
{
    int ret_status;
    ret_status = asm_main();
    return ret_status ;
}

It points to the main asm file that has the following section:

%include "asm_io.inc"
;
; initialized data is put in the .data segment
;
segment .data
;
; These labels refer to strings used for output
;
prompt1 db    "Enter a number: ", 0       ; don't forget nul terminator
prompt2 db    "Enter another number: ", 0
outmsg1 db    "You entered ", 0
outmsg2 db    " and ", 0
outmsg3 db    ", the sum of these is ", 0


;
; uninitialized data is put in the .bss segment
;
segment .bss
;
; These labels refer to double words used to store the inputs
;
input1  resd 1
input2  resd 1

 

;
; code is put in the .text segment
;
segment .text
        global  _asm_main
_asm_main:
        enter   0,0               ; setup routine
        pusha

        mov     eax, prompt1      ; print out prompt
        call    print_string

        call    read_int          ; read integer
        mov     [input1], eax     ; store into input1

        mov     eax, prompt2      ; print out prompt
        call    print_string

        call    read_int          ; read integer
        mov     [input2], eax     ; store into input2

        mov     eax, [input1]     ; eax = dword at input1
        add     eax, [input2]     ; eax += dword at input2
        mov     ebx, eax          ; ebx = eax
        dump_regs 1               ; dump out register values
        dump_mem 2, outmsg1, 1    ; dump out memory
;
; next print out result message as series of steps
;
        mov     eax, outmsg1
        call    print_string      ; print out first message
        mov     eax, [input1]     
        call    print_int         ; print out input1
        mov     eax, outmsg2
        call    print_string      ; print out second message
        mov     eax, [input2]
        call    print_int         ; print out input2
        mov     eax, outmsg3
        call    print_string      ; print out third message
        mov     eax, ebx
        call    print_int         ; print out sum (ebx)
        call    print_nl          ; print new-line

        popa
        mov     eax, 0            ; return back to C
        leave                     
        ret

The asm_io file is located here: https://github.com/nateware/cpp/blob/master/assembly/asm_io.asm

With the use of djgpp mingw(prebuillt 12.2.0) to compile and NASM(2.16.01) as well for asm. The commands I've used include:

i586-pc-msdosdjgpp-gcc.exe -c driver.c
nasm -f coff first.asm
nasm -f coff asm_io.asm

For the final linking i used:

i586-pc-msdosdjgpp-gcc.exe -o first driver.o first.o asm_io.o

Additional things i have done:

Instead of using nasm -f coff first.asm i used nasm -f win32 first.asm
Instead of nasm -f coff asm_io.asm i used nasm -f win32 -d COFF_TYPE asm_io.asm
Finally i did gcc driver.o first.obj asm_io.obj -o first.exe

The following errors now arise:

6 architecture of input file `asm_io.obj' is incompatible with i386:x86-64 output
driver.o:driver.c:(.text+0xe): undefined reference to `asm_main'
asm_io.obj:asm_io.asm:(.text+0x10): undefined reference to `_scanf'
asm_io.obj:asm_io.asm:(.text+0x2a): undefined reference to `_printf'
asm_io.obj:asm_io.asm:(.text+0x41): undefined reference to `_printf'
asm_io.obj:asm_io.asm:(.text+0x52): undefined reference to `_getchar'
asm_io.obj:asm_io.asm:(.text+0x68): undefined reference to `_putchar'
asm_io.obj:asm_io.asm:(.text+0x7a): undefined reference to `_putchar'
asm_io.obj:asm_io.asm:(.text+0x149): undefined reference to `_printf'
asm_io.obj:asm_io.asm:(.text+0x16c): undefined reference to `_printf'
asm_io.obj:asm_io.asm:(.text+0x198): undefined reference to `_printf'
asm_io.obj:asm_io.asm:(.text+0x1bf): undefined reference to `_printf'
asm_io.obj:asm_io.asm:(.text+0x1d8): undefined reference to `_printf'

No matter what I have done to try and get the final exe, I've failed. Any help or something i am missing?

For Further Context: This is from the book PCASM by Paul Carter: https://github.com/nateware/cpp/blob/master/assembly/first.asm Included CDEHL header file: header file The assembly input\output file is located here:I/O

2

There are 2 best solutions below

5
pts On BEST ANSWER

The easiest way to make examples of that book work on your system is using the 32-bit MinGW GCC. As soon as you have it set up, these commands work:

gcc -c driver.c
nasm -f win32 -o first.o first.asm
nasm -f win32 -o asm_io.o asm_io.asm
gcc -o first driver.o first.o asm_io.o
first.exe

If you get an error, please copy-paste all the commands you have run (including gcc -v), and the full output of each to your question, and then add a comment stating that you have extended the question.

Before installing anything to your system (64-bit Windows), remove all of these: DJGPP, MinGW (32-bit and 64-bit), MinGW-w64, MSYS, MSYS2. You can keep NASM.

You can get the 32-bit MinGW GCC by following any (one) of these methods:

  • Directly from MINGW32. (I think this one is easier.)

  • Installing MSYS2, setting the environment variable MSYS=MINGW32 (to select the MINGW32 environment), starting the MSYS2 shell window, installing GCC.

Unfortunately I'm not able to help you with the installation beyond what I aready wrote. If you get stuck, ask a separate question on StackOverflow.

If you are unsure whether your GCC is 32-bit or 64-bit, run gcc -v, and look at the line starting with Target:. If it contains 64, then it's 64-bit. If it contains i386, i486, i586 or i686, then it's 32-bit. You need 32-bit. If you get 64-bit, then you've installed the wrong GCC, you have to uninstall it, and install the right one. If needed, ask a separate question on StackOverflow.

Don't use DJGPP, because it creates DOS .exe files, and those don't work on 64-bit Windows systems such as your system. (You may get an error about missing 16-bit support. It doesn't matter that your .asm files have 32-bit code, DOS .exe files won't work on your system, and DJGPP is not able to create anything else.)

For this project, don't use 64-bit GCC (e.g. in MinGW-w64), because it generates 64-bit Windows .exe files, which require 64-bit .asm files, and your book contains only 32-bit .asm files.

8
pts On

TL;DR Use i586-pc-msdosdjgpp-gcc.exe instead of i586-pc-msdosdjgpp-g++.exe to compile driver.c.

Alternatively, to fix the undefined reference to 'asm_main()' error, declare it with extern "C" in your main C++ file (driver.c):

#ifdef __cplusplus
extern "C"
#endif
int PRE_CDECL asm_main( void ) POST_CDECL;

A bit longer explanation:

  • If you compile your driver.c with i586-pc-msdosdjgpp-g++.exe, then it will treat driver.c as a C++ source file. As an alternative, i586-pc-msdosdjgpp-gcc.exe treats it as a C source file. C and C++ are different programming languages, and compilers are following different rules, there is no 100% backwards compatibility. You have run into one of those differences.
  • Your driver.c is valid in both C and C++, so both compilations will succeed and create driver.o.
  • Name mangling is done by the C and C++ compilers. The C function name asm_main becomes _asm_main in driver.o, and the C++ function name asm_main becomes something like __Z8asm_mainv in driver.o. Run nm driver.o to get the mangled symbol names.
  • To make linking work, you have to use the mangled name in your NASM source (first.asm file).
  • For that, you can compile driver.c with the C compiler i586-pc-msdosdjgpp-gcc.exe and name the function _asm_main in your NASM source.
  • Alternatively, you convince the C++ compiler i586-pc-msdosdjgpp-g++.exe to use C-style name mangling for that function. This can be done adding extern "C" to the C++ source file, see above how.