Correct way to debug assembly code (nasm) with GDB - c

I am trying to learn the x86 assembly language, and would like to set up a little environment to do it efficiently. For that I have written some simple code (factorial, fibonnaci,etc...), added a main.c for testing and a Makefile to compile.
But I was never able to debug my code with gdb, no matter what flags I have tried...
Here is the code of facto.asm
global fact
section .text
fact:
push rbx
mov rcx, rdi
xor rax, rax
xor rbx, rbx
inc rax
inc rbx
loop:
mov rdx, rbx
mul rdx
inc rbx
dec rcx
jnz loop
pop rbx
ret
The Makefile:
EXE := facto
BIN := $(EXE).o
ASM := $(EXE).asm
SRC := main
all: $(EXE)
$(BIN): $(ASM)
nasm -f elf64 -gdwarf $(ASM) -o $(BIN)
$(EXE): $(SRC).c $(BIN)
gcc -no-pie -g $(SRC).c $(BIN) -o $(EXE)
.PHONY: clean
clean:
$(RM) *.o
$(RM) $(EXE)
And finally the main.c in which I am doing the tests:
#include <stdio.h>
extern int fact();
int main(void)
{
size_t res = fact(5);
printf("%zu\n",res);
}
When running GDB and trying to "skip" into the assembler code, it just does not get in there and goes directly to the printf, even though I have set the -g flag for gcc and -gdwarf for nasm. Furthermore I am manually setting a break on that line with b.
What is it that I am missing? I would be glad for any clues!
If this can help by any means,I am currently working on an arch linux machine.
Thanks in advance!

Related

C makefile issue, undefined reference to function

Im trying to do a simple excercise in compilation.
I have 1 c file 1 assembly file and a makefile.
when I run the 'make' command I get the following error:
gcc -g -m32 -Wall -o mainAssignment0.o mainAssignment0.c
/tmp/ccXfVxtg.o: In function `main':
/home/caspl202/Desktop/tasks/Assignment0/mainAssignment0.c:12: undefined reference to `do_Str'
collect2: error: ld returned 1 exit status
makefile:10: recipe for target 'mainAssignment0.o' failed
make: * [mainAssignment0.o] Error 1
Meaning that for some reason the c program doesnt recognize the external ASM function.
Whats even weirder is that when I run the same makefile on the same files on a different machine it works like a charm. I would really like someone to shed some light on this thing.
C code:
#include <stdio.h>
#define MAX_LEN 100
extern int do_Str(char*);
int main(int argc, char** argv) {
char str_buf[MAX_LEN];
int counter = 0;
fgets(str_buf, MAX_LEN, stdin);
counter = do_Str (str_buf);
printf("%s%d\n",str_buf,counter);
return 0;
}
ASM code:
section .data
an: dd 0
section .text
global do_Str
do_Str:
push ebp
mov ebp, esp
pushad
mov ecx, dword [ebp+8]
loop:
cmp byte [ecx], 32
jnz noS
inc dword [an]
noS:
cmp byte [ecx], 65
jl noC
cmp byte [ecx], 90
jg noC
add byte [ecx], 32
noC:
inc ecx
cmp byte [ecx], 0
jnz loop
popad
mov eax,[an]
mov esp, ebp
pop ebp
ret
Makefile:
all: exec
libs: asm-lib
asm-lib: asmAssignment0.s
nasm -g -f elf -o asmAssignment0.o asmAssignment0.s
exec: mainAssignment0.c libs
gcc -g -m32 -c -o mainAssignment0.o mainAssignment0.c
gcc -g -m32 -o Assignment0.out mainAssignment0.o asmAssignment0.o
.PHONY: clean
clean:
rm -rf ./*.o Assignment0.out
You don't need to declare the function external.
int do_Str(char*);
Also, a function in C is prefixed with an underscore, so you must name it accordingly in your asm file.
global _do_Str
_do_Str:
The underscore is automatically added by the C compiler, so you don't have to use it in the C module.
The reason for your error you quote here is that your compile line is wrong. You can tell because you're trying to create an object file, but getting errors from the linker, so something is clearly not right:
gcc -g -m32 -Wall -o mainAssignment0.o mainAssignment0.c
...
collect2: error: ld returned 1 exit status
The problem is you forgot to add the -c flag to this compile line, so that the compiler generates an object file.
However, in your makefile the -c is present, so clearly this error you quote is not generated from the makefile you show us.
exec: mainAssignment0.c libs
gcc -g -m32 -c -o mainAssignment0.o mainAssignment0.c

Loading elf-i386 from my boot loader

I am doing operating system project, until now I have my bootloader running. I can load binary file using bios interuppt, but I am unable to load and call C function from ELF file format:
Here is my C program that I want to finally execute:
//build :: cc -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -c -o kmain.o kmain.c
void kmain(){
int a = 5;
for(;;);
}
Here is assembly code to call kmain()
; build :: nasm -f elf loader.asm
[BITS 32]
[GLOBAL start]
[EXTERN kmain]
section .text
start:
mov eax, 0
call kmain
This is my linker script
ENTRY(start)
and this how I am linking everything together
ld -m elf_i386 -T link.ld -o kernel loader.o kmain.o
Now to call start from my bootloader, I am using e_entry offset field from elf header( 24 byte away from starting address) :
xor edx, edx
mov edx, 24
add edx, IMAGE_PMODE_BASE
add ebx, dword[edx]
add ebx, IMAGE_PMODE_BASE
call ebx
where IMAGE_PMODE_BASE is address of elf file loaded in memory.
My question is "Is This the correct way of loading and calling a function in C in ELF file format?".
Thank you for reading, please help.

Using custom main loader with GCC

I wrote the following loader:
GLOBAL _start
EXTERN main
section .text
_start:
xor ebp, ebp ; ebp = 0
pop esi ; esi = argc
mov ecx, esp ; ecx = argv
and esp, 0xFFFF ; align esp
push ecx ; load argv
push esi ; load argc
call main ; call main
push eax ; exit with main's ret value
mov ebx,0
int 80h
And a short main function, now I'm trying to run compile and link these files using gcc, but using the commands
nasm -f elf32 loader.asm
gcc -c -m32 main.c
gcc -m32 main.o loader.o -o main.out
Results in a multiple definition of _start error. I imagine this is because gcc is trying to link his own _start. How can I prevent this from happening?
You haven't told GCC to not link to the standard startup code, so GCC links to it.
To tell GCC to not link in _start, pass in the -nostartfiles flag to GCC when linking.
Note that the standard libraries (stdlib, stdio, etc) will still be linked in, unless you also use the -nodefaultlibs flag. The -nostdlib flag combines the two.

Calling NASM function in C

I'm trying to learn x86 assembler, and I want to call a NASM function in C. When i run my program I get this error:
Segmentation fault (Core dumped)
I've tried dozens of variations of my simple test function but it stops every time at the same position.
Here are my asm and c files:
div.asm:
global _test
_test:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp+8]
mov ebx, [ebp+12]
div ebx
pop ebp
ret
main.c:
#include <stdio.h>
extern unsigned int test (unsigned int, unsigned int);
int main(void)
{
printf("%d\n", div(85,5));
return 0;
}
I compile & link the files with:
nasm -f elf -o div.o div.asm
gcc -m32 -c -o main.o main.c
gcc -m32 -o run div.o main.o
I use a 64 Bit Linux in a Virtual Machine.
What is my mistake here, and how can I fix it?
You forget to pop ebx (or at least make the stack in order):
push ebp
mov ebp, esp
push ebx ; you push it here
mov eax, [ebp+8]
mov ebx, [ebp+12]
xor edx,edx ; ..and you must zero edx
div ebx
pop ebx ; forgot to pop it here
pop ebp
ret
It is unclear if you ever got your problem solved. In addition to the other issues, you would need to make your function call in your main.c match the call in div.asm. For example if you have created an assembly function _test, you need to declare it as extern and actually use the function in main. e.g.:
#include <stdio.h>
extern unsigned int _test (unsigned int, unsigned int);
int main(void)
{
printf("%d\n", _test (85,5)); /* you are calling div here, not _test */
return 0;
}
(your function name is not the name for your assembly object file div.o -- and as pointed out in the comments, div is an unsigned division declared in stdlib.h along with ldiv, and lldiv)
Your global declaration in your assembly function file must match the name you declared in main. e.g.:
global _test
_test:
push ebp
mov ebp, esp
mov eax, [ebp+8]
xor edx, edx
div dword [ebp+12]
mov esp, ebp
pop ebp
ret
Now, you can compile, link and run your test file:
$ nasm -f elf -o div.o div.asm
$ gcc -m32 -c -o main.o main.c
$ gcc -m32 -o run div.o main.o
$./run
17
or for the compilation/link, simply:
$ nasm -f elf -o div.o div.asm
$ gcc -m32 -o run main.c div.o

Assembly function call from c

I cannot combine my kernel_entry.asm and main.c. My main.c calls an asm function Sum. Both nasm and gcc compiles respective files. However, the linker gives an error.
Kernel_entry.asm:
[bits 32]
[extern _start]
[global _Sum]
....
_Sum:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ecx, [ebp+12]
add eax, ecx
pop ebp
ret
main.c:
....
extern int Sum();
void start() {
....
int x = Sum(4, 5);
....
}
To compile source files, I use following commands:
nasm kernel_entry.asm -f win32 -o kernel_entry.o
gcc -ffreestanding -c main.c -o main.o
....
ld -T NUL -o kernel.tmp -Ttext 0x1000 kernel_entry.o main.o mem.o port_in_out.o screen.o idt.o
Linker gives following error:main.o:main.c:(.text+0xa82): undifened reference to 'Sum'. I tried everything but couldn't find any solution. When I remove asm function call from main.c, it works.
The TL;DR version of the answer is that mixing nasm's -f win32 generates an object file that is not compatible with the GNU toolchain on Windows - you need to use -f elf if you want to link using ld. That is described in NASM's documentation here under sections 7.5 and 7.9.
The hint for me was that by running nm kernel_entry.o generated:
00000000 a .absolut
00000000 t .text
00000001 a #feat.00
U _start
U _Sum
Which basically shows Sum as an undefined symbol. After compiling as ELF, I got:
U _start
00000000 T _Sum
indicating Sum as a recognised symbol in the text section.

Resources