Debugging NASM in VS code - c++

I have a simple C++ program that calls some NASM code:
main.cpp:
#include <iostream>
extern "C" int foo();
int main() {
std::cout << "The result is: " << foo() << std::endl;
return 0;
}
foo.asm:
bits 64
global foo
section .text
foo:
mov rax, 123
inc rax
ret
I can compile everything with CMake
cmake_minimum_required (VERSION 3.15)
project (assembly-x64 LANGUAGES CXX ASM_NASM)
# old school CMAKE to handle NASM formats
if(WIN32)
set(CMAKE_ASM_NASM_FLAGS_DEBUG "-g -F cv8")
set(CMAKE_ASM_NASM_OBJECT_FORMAT win64)
elseif(APPLE)
set(CMAKE_ASM_NASM_FLAGS_DEBUG "-g -F dwarf")
set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64)
else()
set(CMAKE_ASM_NASM_FLAGS_DEBUG "-g -F dwarf")
set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
endif()
add_executable(assembly-x64)
target_compile_features(assembly-x64 PUBLIC cxx_std_17)
target_sources(assembly-x64 PRIVATE main.cpp foo.asm)
and I get the correct result. However, I'd like to be able to debug the assembly code just like I would the C++ code. I can create a breakpoint on the foo function (not using the GUI though), but it doesn't show me the corresponding source location when it pauses. Is there a way around that issue? I'd like to be able to watch registers, etc. Not sure if it's possible in VS code.

Short Answer: No. On linux, launching gdb from vs-code to debug asm code resulted in a quick crash. It doesn't even let you put breakpoints on asm code.
Read the longer answer in case you want to know about the alternatives.
I assume you are not using Linux, because I was unable to build the project on Linux with the cmake you provided. I had to add the following:
...
else()
set(CMAKE_ASM_NASM_FLAGS_DEBUG "-g -F dwarf")
set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
# had to add this to build on linux
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> \
<FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
endif()
...
Ok, so now the project is built and running. However, the bad news is that I was unable to set up "debugging" environment with VS-Code. VS-Code doesn't really have good assembly support to begin with so I am not really surprised here. I tried to configure launch.json multiple times, but everytime I launch GDB, VS-Code crashes. This may or may not happen on Windows or Mac, so I can not answer for those platforms but I assume it will not work there.
I personally just use gdb from terminal directly because it is more powerful and easy to use once you figure it out. I will give you two work flows which you can use instead of Vs-code.
Use terminal
Build your executable
Next launch it using gdb like this:
gdb ./assembly-64 --tui
This will open gdb and bring you to the tui screen.
Lets assume we want to put a break point at foo, for this type:
b foo
Now our breakpoints are all set. We are ready to start our debugging session. Type:
run
It will go and break at foo.
But wait a minute, there are no registers, how should we see them? This is my favourite part about gdb. Type:
layout regs
And you will get a beautiful look window at top showing you all the registers. It will even highlight the registers as they change making it easy for you to monitor changes.
Other than this, Use n to step next, use si to step into. That's pretty much it for the basics. If you want to see the value at some memory location, or a register. Type:
print $rax
There's a lot more to this, but this will give you a quick start.
GUI
I was pleasantly surprised when I discovered that QtCreator can debug asm + cpp files very nicely. Just load up your cmake project and place your breakpoints. You can enable registers pane from Menu->Window->Views->Registers. Screenshot:
There are other guis(for gdb) out there, nemiver, ddd etc.

To debug C++ code and assembly code simultaneously,
you can do this with gobolt online: https://godbolt.org/
If you just want to write assembly and debug in vscode, let me tell you the method I just learned.
There is a great project for this.
https://github.com/newtonsart/vscode-assembly
You should also do these additionally.
32 Bit Support
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386
sudo apt install gcc-multilib
nostdlib gcc argument
(https://stackoverflow.com/a/32855954/6646336)
I used extra gcc argument for some errors.
# .vscode/tasks.json
# find gcc command and add <nostdlib> in two line
... gcc -nostdlib -m64...
... gcc -nostdlib -m32...
But still you can't set a breakpoint in (*.s, *.asm) file. vscode doesn't accept. You must enable in settings (keyword: debug.allowBreakpointsEverywhere = true)
And final step: Set a breakpoint and select your debugging type.

Related

Cannot access memory when debugging xv6 user program using gdb

I am currently self-studying 2020 MIT 6.S081: Operating System Engineering https://pdos.csail.mit.edu/6.828/2020/schedule.html. I have followed all the steps for MAC OS to set up the environment correctly.
When I launched gdb and qemu together, I was able to break points normally when debugging the kernel executable.
However, when I attempted to do the same thing for the user executables, I was unable to break any points with the error:
Cannot access memory at address 0x...
It turned out I can set break points for some particular lines, but when I hit continue, another error has shown in the screenshot above.
Any way to get around this? Thank you!
I'm also self-studying 6.S081 and have had the same issue. I solved this just now so try this.
In Makefile, add -gdwarf-2 option to CFLAGS variable:
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2
In my Makefile it's on line 94, and I didn't change other options.
I find this solution from here:
http://staff.ustc.edu.cn/~bjhua/courses/ats/2014/hw/hw-interface.html (search 'error reading variable' in the page)
https://www.reddit.com/r/RISCV/comments/plgwyk/comment/hcalnf1/?utm_source=share&utm_medium=web2x&context=3
and one more thing: If you successfully solve the problem with this but fail to watch variables in gdb, try chainging -O option for CFLAGS to -O0. It would prevent your code from being optimized (ref). (For me, this doesn't work. my code start to stuck frequently after changing this option so I just quit watching variables)
You can check out whether your gdb has warned you can't use file .gdbinit to start gdb? If not, you can simply add a line to .gdbinit: set riscv use-compressed-breakpoints yes,like this:
set confirm off
set architecture riscv:rv64
target remote 127.0.0.1:26000
symbol-file kernel/kernel
set disassemble-next-line auto
set riscv use-compressed-breakpoints yes
then use riscv64-unknown-elf-gdb to start gdb.You will see it work well.
But if you have the warning, then you should use
riscv64-unknown-elf-gdb -iex 'add-auto-load-safe-path .' to start gdb additionally.

GDB and NS2: how to stop program at some Function call

I am using gdb to debug NS-2 which is a simulator for network protocols. It takes an .tcl file as input and interpret it. [I think it is an interpreter.]
Some of the code is written in tcl (events and creation of network components) and some in C++ (especially Packet Formats, Agents etc.).
I have created an Agent in C++ and i want to stop it at some function call so that i can see the stack trace and find which other classes have been called before it.
This is what i have done:
There was some error in one of my MyAgent::function and it was giving Segmentation Fault and gdb was stopping there automatically. I could then see the stack trace. I rectified the error.
Now when i run
gdb ./ns
b MyAgent::function()
/*
When i press TAB after writing "b MyA" it gives me all functions
of my class :). when i press enter after above command --
it asks me "Breakpoint on future shared library load" and i say Yes.
I hope this is ok ??
*/
r myfiles/myWireless.tcl
Now it runs and do not stop anywhere. :(
I am sure that this function is being called, because when that Segmentation fault was occuring, it was stopping at that function.
Thanks
You can add a breakpoint in that function:
(gdb) break MyAgent::function()
You must make sure to compile with whatever options are necessary to get debug symbols. On GCC, use the -g or -ggdb options.
You need the -args option to specify the tcl script that will be executed.
Run gdb like this:
gdb -args ./ns path/to/tcl/script.tcl
To enable debug flag to c++ code, if have not done it already, re-configure your ns2 instalation with:
./configure --enable-debug ;# plus any other flags you use for configuring
make clean
make -j 3 ;# -j for faster compiling
make install ;# optional
You can also use the --with-tcldebug=..., for debugging tcl code (You need to install tcldebug first for this option)

How to load extra libraries for GDB?

I'm trying to debug a CUDA program, but when I'm launching gdb like so:
$ gdb -i=mi <program name>
$ r <program arguments>
I'm getting:
/home/wvxvw/Projects/cuda/exercise-1-udacity/cs344/HW2/hw:
error while loading shared libraries: libcudart.so.5.0:
cannot open shared object file: No such file or directory
Process gdb-inferior killed
(formatted for readability)
(I'm running gdb using M-xgdb) If that matters, then CUDA libraries are in the .bashrc
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64"
error while loading shared libraries: libcudart.so.5.0
This error has nothing to do with GDB: your executable, when run from inside GDB, can't find the library it needs.
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64"
GDB runs your program in a new $SHELL, so that should have worked. I wonder if there is some interaction with emacs.
In any case, this:
(gdb) set env LD_LIBRARY_PATH /usr/local/cuda/lib64
(gdb) run
should fix this problem.
Update:
as I've mentioned it before, ld path is set properly
No, it isn't. If it was, you wouldn't have the problem.
Now, I don't know why it isn't set properly. If you really want to find out, start by running GDB outside emacs (to exclude possible emacs interactions).
If the problem is still present, gdb show env, shell env, adding echo "Here" to your ~/.basrc, etc. should help you find where things are not working as you expect them.
I've had this problem as well. One way to look at it is that even if the LD_LIBRARY_PATH variable is correct when you enter show env into gdb, it may not be correct when you actually execute the program because gdb executes $SHELL -c <program> to run the program. Try this as a test, run $SHELL from the command line and then echo $LD_LIBRARY_PATH. Is it correct? If not, then you probably need to add it to your rc (.tcshrc in my case).
I had a similar problem when trying to run gdb on windows 7. I use MobaXterm to access a Linux toolbox. I installed gdb separately from http://www.gnu.org/software/gdb/ . I got it to work by making sure gdb could find the correct .dll files as mentioned by Employed Russian. If you have MobaXterm installed the .dll files should appear in your home directory in MobaXterm/slash/bin.
gdb however did not recognize the LD_LIBRARY_PATH variable. For me, it worked when I used the PATH variable instead:
(gdb) set env PATH C:\Users\Joshua\Documents\MobaXterm\slash\bin
(gdb) run
I would think using PATH instead of LD_LIBRARY_PATH might work for you provided you put the correct path to your library.
gdb is looking for a library, so why are you concerned with the include path? You may want to try to set the gdb option "solib-search-path" to point to the location of the libcudart.so.5.0 library.

GDB Loading the incorrect library

I have two different version of a library let's say
libxyz.so
at two different location
1) /home/maverick/dev/libxyz.so ( development Version)
2) /home/maverick/prod/libxyz.so ( Production Version )
I have a setup that compile my program and link with appropriate version of the library depending on LD_LIBRARY_PATH. for example If I want to link my program with dev version of libxyz.so i change my LD_LIBRARY_PATH to add /home/maverick/dev and if I want to link with prod version I change LD_LIBRARY_PATH to add /home/maverick/prod instead.
I compiled my program by linking with dev version and the output of
ldd MyProg
is
libxyz.so => /home/maverick/dev/libxyz.so
If i run the program it loads the libxyz.so from
/home/maverick/dev/libxyz.so
and runs fine.
it this point my LD_LIBRARY_PATH includes /home/maverick/dev not /home/maverick/prod
but when I try to debug this program through GDB
gdb MyProg
it loads the libxyz.so from location
/home/maverick/prod/libxyz.so
I have trouble making GDB load the correct version of the library during debug. So till now what I am doing is first launch the program (that load the dev version) and attach gdb to that PID this way its works fine. but if it run like
gdb MyProg
it dosen't
I tried every thing to fix this like setting up sysroot, solib-search-path in GDB but nothing is working.
if fact when I set up sysroot to point to the debug version of the library gdb gave some message like
.dynamic section for XXX is not at the expected address
any suggestion would be appreciated.
I have trouble making GDB load the correct version of the library during debug.
Let me guess: you are using tcsh, right?
The problem most likely comes from yoour ~/.cshrc resetting LD_LIBRARY_PATH to /home/maverick/prod.
When you run the program in GDB, it executes $SHELL -c your-program [args...] (so as to allow you to use I/O redirection).
Solution: don't touch environment in your .cshrc for non-interactive shell, e.g. start it with:
if ($?prompt == 0) exit

How to compile a C++ program as 64-bit on 64-bit machine?

Perhaps a very trivial question:
I need to compile a program as 64-bit (earlier makefile written to compile it as 32-bit).
I saw the option -m32 appearing in command line parameters with each file compilation. So, I modified the makefile to get rid of -m32 in OPTFLAG, but again when the program compiles, I still see -m32 showing up and binaries are still 32-bit. Does this m32 come from somewhere else as well?
-m32 can only be coming from somewhere in your makefiles, you'll have to track it down (use a recursive grep) and remove it.
When I am able to force -m64, I get "CPU you selected does not support x86-64 instruction set".Any clues?. uname -a gives x86_64
That error means there is an option like -march=i686 in the makefiles, which is not valid for 64-bit compilation, try removing that too.
If you can't remove it (try harder!) then adding -march=x86-64 after it on the command line will specify a generic 64-bit CPU type.
If the software you are trying to build is autotools-based, this should do the trick:
./configure "CFLAGS=-m64" "CXXFLAGS=-m64" "LDFLAGS=-m64" && make
Or, for just a plain Makefile:
env CFLAGS=-m64 CXXFLAGS=-m64 LDFLAGS=-m64 make
If you are using CMake, you can add m64 compile options by this:
add_compile_options(-m64)