NASM: undefined reference to `print_string' and similar erros - c

I have been reading from Paul A. Carter's PC Assembly Language book to learn assembly and got to a part where there is actual coding. I have limited assembly knowledge, and have only made small programs using MASM in the past. I installed NASM and I have been coding assembly in VS code. I have a MinGW file in my C: drive that is the path (I am not 100% sure if that is the correct terminology).
I have been trying to get this code to work
; file: first.asm
; First assembly program. This program asks for two integers as
; input and prints out their sum.
;
; To create executable using djgpp:
; nasm -f coff first.asm
; gcc -o first first.o driver.c asm_io.o
%include "asm_io.inc"
extern print_string
;
; 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 null 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 ; print out register values
dump_mem 2, outmsg1, 1 ; print 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
This is the code from the book itself. I noticed the GitHub for the course has another "first" program but that one only gave me more trouble and seemed to be different from the one in the book. I call a batch file to run the program:
set ProjectName=first
nasm -f win32 %ProjectName%.asm
gcc %ProjectName%.obj -o %ProjectName%.exe
%ProjectName%.exe
This is what outputs after running this command:
C:\Users\jackg\OneDrive\Desktop\CS & ADL\assembly\assembly1>compileNASM.bat
C:\Users\jackg\OneDrive\Desktop\CS & ADL\assembly\assembly1>set ProjectName=first
C:\Users\jackg\OneDrive\Desktop\CS & ADL\assembly\assembly1>nasm -f win32 first.asm
C:\Users\jackg\OneDrive\Desktop\CS & ADL\assembly\assembly1>gcc first.obj -o first.exe
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0xb): undefined reference to `print_string'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x10): undefined reference to `read_int'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x1f): undefined reference to `print_string'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x24): undefined reference to `read_int'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x3d): undefined reference to `sub_dump_regs'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x4b): undefined reference to `sub_dump_mem'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x55): undefined reference to `print_string'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x5f): undefined reference to `print_int'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x69): undefined reference to `print_string'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x73): undefined reference to `print_int'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x7d): undefined reference to `print_string'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x84): undefined reference to `print_int'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: first.obj:first.asm:(.text+0x89): undefined reference to `print_nl'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../libmingw32.a(main.o):(.text.startup+0xc0): undefined reference to `WinMain#16'
collect2.exe: error: ld returned 1 exit status
C:\Users\jackg\OneDrive\Desktop\CS & ADL\assembly\assembly1>first.exe
'first.exe' is not recognized as an internal or external command,
operable program or batch file.
Additionally I have the asm_io.asm and asm_io.inc files in the same location as the first.asm file. I also ran nasm -f win32 -d COFF_TYPE asm_io.asm to make the object file.
I am not sure where to go from here. I have searched around inline for answers on how to set everything up or what might be wrong but I can't find any. Let me know how I could fix this and any tips or necessary components needed for the book!

Related

Linking a c function and a asm assembly file

I have a postfix program that does a while loop and prints 10 numbers, and it needs an extern print function which i defined, but for some reason after i create the object file if i do ld -m elf_i386 -s -o p11 p11.o print.o it gives me some strange errors.
what i want to do is link those files and create an executable that prints values from 1 to 10, but for some reason its giving me those errors when clearly what i want is use that c function to print those values
ps: if i use gcc -m32 -o p11 p11.o print.o i get the following error:
/usr/bin/ld: p11.o: warning: relocation in read-only section `.text'
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib32/Scrt1.o: in function `_start':
(.text+0x22): undefined reference to `main'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
commands:
$ nasm -f elf32 p11.asm -o p11.o
$ gcc -m32 -o print.o -c print.c
$ ld -m elf_i386 -s -o p11 p11.o print.o
Error:
ld: warning: cannot find entry symbol _start; defaulting to 0000000008049000
ld: print.o: in function `print':
print.c:(.text+0x21): undefined reference to `printf'
I dont get why because printf is defined in c and clearly coded somewhere
postfix program:
EXTERN print ; extern print(int)
DATA
ALIGN
GLOBAL ix, OBJ; static ix
LABEL ix
SINT 0; static ix=0
TEXT
ALIGN
INT 0
DUP32
ADDR ix
STINT
TRASH 4
ALIGN
LABEL whilecond
ADDR ix
LDINT
INT 10
LT
JZ endwhile
ADDR ix
LDINT
INT 1
ADD
DUP32
ADDR ix
STINT
TRASH 4
ADDR ix
LDINT
CALL print
TRASH 4
JMP whilecond
ALIGN
LABEL endwhile
assembly program:
extern print
segment .data
align 4
global ix:object
ix:
dd 0
segment .text
align 4
push dword 0
push dword [esp]
push dword $ix
pop ecx
pop eax
mov [ecx], eax
add esp, 4
align 4
whilecond:
push dword $ix
pop eax
push dword [eax]
push dword 10
pop eax
xor ecx, ecx
cmp [esp], eax
setl cl
mov [esp], ecx
pop eax
cmp eax, byte 0
je near endwhile
push dword $ix
pop eax
push dword [eax]
push dword 1
pop eax
add dword [esp], eax
push dword [esp]
push dword $ix
pop ecx
pop eax
mov [ecx], eax
add esp, 4
push dword $ix
pop eax
push dword [eax]
call print
add esp, 4
jmp dword whilecond
align 4
endwhile:
c program:
#include <stdio.h>
void print(int num)
{
printf("%d\n",num);
}
I dont get why because printf is defined in c and clearly coded somewhere
Because you are not linking that somewhere (namely, you are not linking with libc).
On UNIX systems, you should never use ld to link anything (with the exceptions of the kernel and boot loader).
Instead you should always use appropriate compiler driver (gcc here). Gcc will automatically add -lc to the link line.
P.S. you should also define the main program -- it's unclear how you expect your code to be invoked, since you defined nighter main, nor _start symbol.
if i use gcc -m32 -o p11 p11.o print.o i get the following error:
Your gcc is configured to build PIE binaries by default, but your assembly is not written to be compatible with PIE.
Add the -fno-pie to your compile lines and -no-pie flag to your link line.

Assembly NASM x86 - Putting char into array

I need to make a program that open a file, read it character by character and save it into an array in Assembly NASM x86. Currently, the program is able to open a file using stdin and read the character by using getchar(). However, i am stuck on the saving the char into an array and need help on this part.
Thank you
; Run it this way:
; test < (input file)
; Build using these commands:
; nasm -f elf -g -F stabs test.asm
; gcc –m32 -o test.o test
;
SECTION .bss ; Section containing uninitialized data
TextLenght EQU 1024 ; Define length of a line of text data
Text resb TextLenght ; Define array
SECTION .data ; Section containing initialised data
fileFmt: db "%c",0
SECTION .text ; Section containing code
extern putchar
extern getchar
extern printf
global main; Linker needs this to find the entry point!
main :
start:
nop ; This no-op keeps gdb happy...
mov eax, 0
mov edx, 0
mov ecx, 0
mov ebx, 0
; Read a buffer full of text from stdin:
read:
call getchar ; call getchar to get input from stdin, char is save in eax
cmp al, -1
jle Done ; if return -1, we at EOF
cmp eax, 97 ; get rid of 'enter' char
jl read
;mov Text, eax; try to save char in eax into array, don;t work
push eax ;push eax to print
push fileFmt
call printf
add esp, 8 ; clear the stack
jmp read
Done:
mov eax,1 ; Code for Exit Syscall
mov ebx,0 ; Return a code of zero
int 80H ; Make sys_exit kernel call

NASM Assembly while loop counter

I'm writing a while loop in assembly to compile in the Linux terminal with nasm and gcc. The program compares x and y until y >= x and reports number of loops at the end. Here's the code:
segment .data
out1 db "It took ", 10, 0
out2 db "iterations to complete loop. That seems like a lot.", 10, 0
x db 10
y db 2
count db 0
segment .bss
segment .text
global main
extern printf
main:
mov eax, x
mov ebx, y
mov ecx, count
jmp lp ;jump to loop lp
lp:
cmp ebx, eax ;compare x and y
jge end ;jump to end if y >= x
inc eax ;add 1 to x
inc ebx ;add 2 to y
inc ebx
inc ecx ;add 1 to count
jp lp ;repeat loop
end:
push out1 ;print message part 1
call printf
push count ;print count
call printf
push out2 ;print message part 2
call printf
;mov edx, out1 ;
;call print_string ;
;
;mov edx, ecx ;these were other attempts to print
;call print_int ;using an included file
;
;mov edx, out2 ;
;call print_string ;
This is compiled and run in the terminal with:
nasm -f elf test.asm
gcc -o test test.o
./test
Terminal output comes out as:
It took
iterations to complete loop. That seems like a lot.
Segmentation fault (core dumped)
I can't see anything wrong with the logic. I think it's syntactical but we've only just started learning assembly and I've tried all sorts of different syntax like brackets around variables and using ret at the end of a segment, but nothing seems to work. I've also searched for segmentation faults but I haven't found anything really helpful. Any help would be appreciated because I'm an absolute beginner.
The reason it crashes is probably that your main function doesn't have a ret instruction. Also be sure to set eax to 0 to signal success:
xor eax, eax ; or `mov eax, 0` if you're more comfortable with that
ret
Additionally, global variables designate pointers, not values. mov eax, x sets eax to the address of x. You need to write back to it if you want anything to happen (or not use global variables).
Finally, you're calling printf with a single non-string argument:
push count ;print count
call printf
The first argument needs to be a format string, like "%i". Here, count is a pointer to a null byte, so you get nothing instead. Off my head, you should try this:
out3 db "%i ", 0
; snip
push ecx
push out3
call printf
I think your problem might just be that you are referencing the addresses of your constants and not their intrinsic value. One must think of a label in nasm as a pointer rather than a value. To access it you just need to use [label]:
segment .data
x dw 42
segment .text
global main
extern printf
main:
mov eax, x
push eax
call printf ; will print address of x (like doing cout<<&x in C++)
mov eax, [x]
push eax
call printf ; will print 42
sub esp, 8
xor eax, eax
ret
PS:I don't think anyone has mentioned it but volatile registers are modified very often when calling external code (C or C++ or other) since at compilation those functions you use are "translated" to assembly and then linked with your asm file. The PC is not a human so it is not distinguishing between what was written in high-level or low-level, the processor is just reading opcodes and operands stored in registers and memory, hence why an external function when using low-level language (call printf) is going to modify (or not! always depends on compiler and architecture) registers that you are also using.
To solve this there are various solutions:
You check what registers are not being modified by using gcc your_c_file.c -S and then in the file your_c_file.swill be the pre-prepared assembly code your compiler has produced from your C file. (It tends to be quite hard to figure out what is what and if you are going to use this method check out Name Mangling, to see how func names will be changed.)
Push all the registers you want to save to stack, and then after the call pop them back to their registers keeping in mind LIFO method.
Use the instructions PUSHA and POPAwhich push or pop all registers respectively.
This is the NASM manual chapter 3 which explains the basis of the language to use: http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html
Hope you managed to solve it.

Segfault while calling C function (printf) from Assembly

I am using NASM on linux to write a basic assembly program that calls a function from the C libraries (printf). Unfortunately, I am incurring a segmentation fault while doing so. Commenting out the call to printf allows the program to run without error.
; Build using these commands:
; nasm -f elf64 -g -F stabs <filename>.asm
; gcc <filename>.o -o <filename>
;
SECTION .bss ; Section containing uninitialized data
SECTION .data ; Section containing initialized data
text db "hello world",10 ;
SECTION .text ; Section containing code
global main
extern printf
;-------------
;MAIN PROGRAM BEGINS HERE
;-------------
main:
push rbp
mov rbp,rsp
push rbx
push rsi
push rdi ;preserve registers
****************
;code i wish to execute
push text ;pushing address of text on to the stack
;x86-64 uses registers for first 6 args, thus should have been:
;mov rdi,text (place address of text in rdi)
;mov rax,0 (place a terminating byte at end of rdi)
call printf ;calling printf from c-libraries
add rsp,8 ;reseting the stack to pre "push text"
**************
pop rdi ;preserve registers
pop rsi
pop rbx
mov rsp,rbp
pop rbp
ret
x86_64 does not use the stack for the first 6 args. You need to load them in the proper registers. Those are:
rdi, rsi, rdx, rcx, r8, r9
The trick I use to remember the first two is to imagine the function is memcpy implemented as rep movsb,
You're calling a varargs function -- printf expects a variable number of arguments and you have to account for that in the argument stack. See here: http://www.csee.umbc.edu/portal/help/nasm/sample.shtml#printf1

how to call C functions from assembly routines and link the C and assembly files using nasm and gcc

I want to call at least 1 C function from assembly. It is because I'm doing my own tiny OS from scratch(out of nothing). The reason i want to call c function from my boot loader. I can understand assembly but poor in writing my own program. So if i could transfer control from assembly procedure to c procedure my job is made easier.
So how to link assembly pgm and C program files into one. It is ok for me even if the file size exceeds 512 bytes.
I am doing this on Windows 7 with help of mingw. my c compiler is gcc and assembler is nasm.
easier to just show you an example, I found this on the internet a while ago and saved it as a source on my computer, not sure where from though
; printf1.asm print an integer from storage and from a register
; Assemble: nasm -f elf -l printf.lst printf1.asm
; Link: gcc -o printf1 printf1.o
; Run: printf1
; Output: a=5, eax=7
; Equivalent C code
; /* printf1.c print an int and an expression */
; #include
; int main()
; {
; int a=5;
; printf("a=%d, eax=%d\n", a, a+2);
; return 0;
; }
; Declare some external functions
;
extern printf ; the C function, to be called
SECTION .data ; Data section, initialized variables
a: dd 5 ; int a=5;
fmt: db "a=%d, eax=%d", 10, 0 ; The printf format, "\n",'0'
SECTION .text ; Code section.
global main ; the standard gcc entry point
main: ; the program label for the entry point
push ebp ; set up stack frame
mov ebp,esp
mov eax, [a] ; put a from store into register
add eax, 2 ; a+2
push eax ; value of a+2
push dword [a] ; value of variable a
push dword fmt ; address of ctrl string
call printf ; Call C function
add esp, 12 ; pop stack 3 push times 4 bytes
mov esp, ebp ; takedown stack frame
pop ebp ; same as "leave" op
mov eax,0 ; normal, no error, return value
ret ; return

Resources