I am completely new to NASM assembly on X86_64.I would like to access my variable toto in an asm programm and increment its value. So far I'm doing the following:
C program:
#include <stdio.h>
#include <inttypes.h>
int64_t toto;
extern void modifytoto(void);
int main() {
toto=0;
modifytoto();
printf("toto = %d \n",toto);
return 0;
}
and the assembly program is the following (the incrementation is pseudo-code).
global modifytoto
global toto
section .text
modifytoto:
mov rax, 1
mov toto, rax
ret
I can't use toto as an argument to modifytoto() because this is supposed to be used in a more complex program in which I don't wan't to modify the arguments.
I'm assemblying with the following cmd
nasm -f elf64 -o mix_asm.o kernel3.asm
and I'm getting this message:
kernel3.asm:7: error: symbol `toto' undefined
What is wrong with my code ?
You need to put the line
extern toto
somewhere before you use toto to tell NASM that toto is an external symbol. That's like in C: The compiler has no idea what toto is supposed to be if you don't tell it by declaring toto.
Related
I am trying to run assembly code inside C code(on CLion). I defined variable x outside of assembly insert and tried to mov a number into it but compiler says x is undefined. I don't get how to make it see variables. Also I have to use intel syntax.
int main(int argc, char** argv) {
short int x = 0;
__asm__ (
".intel_syntax noprefix\n\t"
"mov eax, 0x02\n\t"
"mov x, eax\n\t"
".att_syntax prefix\n\t"
);
printf("%d", x);
}
And there is error
[ 50%] Building CXX object CMakeFiles/ass_lab_2.dir/main.cpp.o
[100%] Linking CXX executable ass_lab_2
/usr/bin/ld: CMakeFiles/ass_lab_2.dir/main.cpp.o: relocation R_X86_64_32S against undefined symbol `x' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
...
P.S. I solved problem. This link was extremely helpful. You just need to pass your variables into asm function using it's syntax.
(Editor's note: the first couple pages of that, about using GNU C Basic asm statements to modify global variables, are unsafe (because there's no "memory" clobber) and only happen to work. The only safe way to modify C variables is with Extended asm with input/output operands; later sections of that article cover that.)
Your main issue is that x is a local variable. You will have to use extended assembly to modify it (and use -masm=intel to use Intel syntax):
int main(void)
{
short int x = 0;
__asm__("mov %0, 0x02\n\t" : "=r"(x));
printf("%d", x);
}
Also, you can use AT&T syntax. It will look like this:
int main(void)
{
short int x = 0;
__asm__("mov $0x02, %0\n\t" : "=r"(x));
printf("%d", x);
}
Because I'm using the =r constraint here, this will be stored in a register; therefore, you don't need to use eax (which should be ax, by the way) as an intermediate storage place to store the value.
I tried to write a char[] in stdout using inline NASM (note .intel_syntax and .att_syntax are added so it can be compiled with gcc)
but it doesn't write anything in stdout.
I am using Linux 16 (x86) on Virtual machine
Is it cause of char c[] ? ( I read by this way of compiling we cannot use memory variables but what to do instead?)
#include<stdio.h>
char c[] = "abcd";
int main(){
asm (".intel_syntax noprefix");
// write c into stdout
asm("mov EAX,4"); // 4: write
asm("mov EBX,1"); // 1: stdout
asm("mov ECX,c");
asm("mov EDX,5");
asm("int 0x80");
// exit program
asm("mov EAX,1")
asm("int 0x80")
asm (".att_syntax noprefix");
}
the output is nothing
The GNU assembler (which is what gcc uses) does not use NASM syntax. It rather uses a variant of Microsoft's MASM syntax where no brackets are needed to dereference a variable. Since you don't want to load the value of the c variable but rather its address, you need an offset keyword:
mov ecx, offset c
I strongly recommend you to avoid inline assembly as much as possible for learning assembly. Using inline assembly in gcc requires good knowledge of how exactly this whole thing works and writing random instructions usually leads to wrong code. Even your simple code is already fundamentally broken and would not work if it was any more complicated than that (so the compiler had a chance to try to use the registers you overwrote without telling).
Instead, put your assembly in a separate file and link it in. This sidesteps all issues you have with inline assembly and allows you to use NASM as you wanted. For example, try something like this:
main.c
char c[] = "abcd";
/* the function you define in print_c.asm */
extern void print_c();
int main() {
print_c(); /* call your assembly function */
}
print_c.asm
; pull in c defined in main.c
extern c
section .text
global print_c
print_c:
; write c to stdout
mov eax, 4
mov ebx, 1
mov ecx, c
mov edx, 5
int 0x80
; exit program
mov eax, 1
int 0x80
Then assemble, compile, and link with:
nasm -felf print_c.asm
cc -m32 -o print_c print_c.o main.c
Here is a minimal example for an "executable" shared library (assumed file name: mini.c):
// Interpreter path is different on some systems
//+definitely different for 32-Bit machines
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#include <stdio.h>
#include <stdlib.h>
int entry() {
printf("WooFoo!\n");
exit (0);
}
If one compiles it with e.g.: gcc -fPIC -o mini.so -shared -Wl,-e,entry mini.c. "Running" the resulting .so will look like this:
confus#confusion:~$ ./mini.so
WooFoo!
My question is now:
How do I have to change the above program to pass command line arguments to a call of the .so-file? An example shell session after the change might e.g. look like this:
confus#confusion:~$ ./mini.so 2 bar
1: WooFoo! bar!
2: WooFoo! bar!
confus#confusion:~$ ./mini.so 3 bla
1: WooFoo! bla!
2: WooFoo! bla!
3: WooFoo! bla!
5: WooFoo! Bar!
It would also be nice to detect on compile time, wheter the target is a 32-Bit or 64-Bit binary to change the interpreter string accordingly. Otherwise one gets a "Accessing a corrupted shared library" warning. Something like:
#ifdef SIXTY_FOUR_BIT
const char my_interp[] __attribute__((section(".interp"))) = "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#else
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
#endif
Or even better, to detect the appropriate path fully automatically to ensure it is right for the system the library is compiled on.
How do I have to change the above program to pass command line arguments to a call of the .so-file?
When you run your shared library, argc and argv will be passed to your entry function on the stack.
The problem is that the calling convention used when you compile your shared library on x86_64 linux is going to be that of the System V AMD64 ABI, which doesn't take arguments on the stack but in registers.
You'll need some ASM glue code that fetches argument from the stack and puts them into the right registers.
Here's a simple .asm file you can save as entry.asm and just link with:
global _entry
extern entry, _GLOBAL_OFFSET_TABLE_
section .text
BITS 64
_entry:
mov rdi, [rsp]
mov rsi, rsp
add rsi, 8
call .getGOT
.getGOT:
pop rbx
add rbx,_GLOBAL_OFFSET_TABLE_+$$-.getGOT wrt ..gotpc
jmp entry wrt ..plt
That code copies the arguments from the stack into the appropriate registers, and then calls your entry function in a position-independent way.
You can then just write your entry as if it was a regular main function:
// Interpreter path is different on some systems
//+definitely different for 32-Bit machines
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#include <stdio.h>
#include <stdlib.h>
int entry(int argc, char* argv[]) {
printf("WooFoo! Got %d args!\n", argc);
exit (0);
}
And this is how you would then compile your library:
nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o
The advantage is that you won't have inline asm statements mixed with your C code, instead your real entry point is cleanly abstracted away in a start file.
It would also be nice to detect on compile time, wheter the target is a 32-Bit or 64-Bit binary to change the interpreter string accordingly.
Unfortunately, there's no completely clean, reliable way to do that. The best you can do is rely on your preferred compiler having the right defines.
Since you use GCC you can write your C code like this:
#if defined(__x86_64__)
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#elif defined(__i386__)
const char my_interp[] __attribute__((section(".interp")))
= "/lib/ld-linux.so.2";
#else
#error Architecture or compiler not supported
#endif
#include <stdio.h>
#include <stdlib.h>
int entry(int argc, char* argv[]) {
printf("%d: WooFoo!\n", argc);
exit (0);
}
And have two different start files.
One for 64bit:
global _entry
extern entry, _GLOBAL_OFFSET_TABLE_
section .text
BITS 64
_entry:
mov rdi, [rsp]
mov rsi, rsp
add rsi, 8
call .getGOT
.getGOT:
pop rbx
add rbx,_GLOBAL_OFFSET_TABLE_+$$-.getGOT wrt ..gotpc
jmp entry wrt ..plt
And one for 32bit:
global _entry
extern entry, _GLOBAL_OFFSET_TABLE_
section .text
BITS 32
_entry:
mov edi, [esp]
mov esi, esp
add esi, 4
call .getGOT
.getGOT:
pop ebx
add ebx,_GLOBAL_OFFSET_TABLE_+$$-.getGOT wrt ..gotpc
push edi
push esi
jmp entry wrt ..plt
Which means you now have two slightly different ways to compile your library for each target.
For 64bit:
nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o -m64
And for 32bit:
nasm entry32.asm -f elf32
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry32.o -m32
So to sum it up you now have two start files entry.asm and entry32.asm, a set of defines in your mini.c that picks the right interpreter automatically, and two slightly different ways of compiling your library depending on the target.
So if we really want to go all the way, all that's left is to create a Makefile that detects the right target and builds your library accordingly.Let's do just that:
ARCH := $(shell getconf LONG_BIT)
all: build_$(ARCH)
build_32:
nasm entry32.asm -f elf32
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry32.o -m32
build_64:
nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o -m64
And we're done here. Just run make to build your library and let the magic happen.
Add
int argc;
char **argv;
asm("mov 8(%%rbp), %0" : "=&r" (argc));
asm("mov %%rbp, %0\n"
"add $16, %0" : "=&r" (argv));
to the top of your entry function. On x86_64 platforms, this will give you access to the arguments.
The LNW article that John Bollinger linked to in the comments explains why this code works. It might interest you why this is not required when you write a normal C program, or rather, why it does not suffice do just give your entry function the two usual int argc, char **argv arguments: The entry point for a C program normally is not the main function, but instead an assembler function by glibc that does some preparations for you - among others fetch the arguments from the stack - and that eventually (via some intermediate functions) calls your main function. Note that this also means that you might experience other problems, since you skip this initialization! For some history, the cdecl wikipedia page, especially on the difference between x86 and x86_64, might be of further interest.
Trying to deal with my assignment of Assembly Language...
There are two files, hello.c and world.asm, the professor ask us to compile the two file using gcc and nasm and link the object code together.
I can do it under 64 bit ubuntu 12.10 well, with native gcc and nasm.
But when I try same thing on 64 bit Win8 via cygwin 1.7 (first I try to use gcc but somehow the -m64 option doesn't work, and since the professor ask us to generate the code in 64-bit, I googled and found a package called mingw-w64 which has a compiler x86_64-w64-mingw32-gcc that I can use -m64 with), I can get the files compiled to mainhello.o and world.o and link them to a main.out file, but somehow when I type " ./main.out" and wait for the "Hello world", nothing happens, no output no error message.
New user thus can't post image, sorry about that, here is the screenshot of what happens in the Cygwin shell:
I'm just a newbie to everything, I know I can do the assignment under ubuntu, but I'm just being curious about what's going on here?
Thank you guys
hello.c
//Purpose: Demonstrate outputting integer data using the format specifiers of C.
//
//Compile this source file: gcc -c -Wall -m64 -o mainhello.o hello.c
//Link this object file with all other object files:
//gcc -m64 -o main.out mainhello.o world.o
//Execute in 64-bit protected mode: ./main.out
//
#include <stdio.h>
#include <stdint.h> //For C99 compatability
extern unsigned long int sayhello();
int main(int argc, char* argv[])
{unsigned long int result = -999;
printf("%s\n\n","The main C program will now call the X86-64 subprogram.");
result = sayhello();
printf("%s\n","The subprogram has returned control to main.");
printf("%s%lu\n","The return code is ",result);
printf("%s\n","Bye");
return result;
}
world.asm
;Purpose: Output the famous Hello World message.
;Assemble: nasm -f elf64 -l world.lis -o world.o world.asm
;===== Begin code area
extern printf ;This function will be linked into the executable by the linker
global sayhello
segment .data ;Place initialized data in this segment
welcome db "Hello World", 10, 0
specifierforstringdata db "%s", 10,
segment .bss
segment .text
sayhello:
;Output the famous message
mov qword rax, 0
mov rdi, specifierforstringdata
mov rsi, welcome
call printf
;Prepare to exit from this function
mov qword rax, 0
ret;
;===== End of function sayhello
;Purpose: Output the famous Hello World message.
;Assemble: nasm -f win64 -o world.o world.asm
;===== Begin code area
extern _printf ;This function will be linked into the executable by the linker
global _sayhello
segment .data ;Place initialized data in this segment
welcome db "Hello World", 0
specifierforstringdata db "%s", 10, 0
segment .text
_sayhello:
;Output the famous message
sub rsp, 40 ; shadow space and stack alignment
mov rcx, specifierforstringdata
mov rdx, welcome
call _printf
add rsp, 40 ; clean up stack
;Prepare to exit from this function
mov qword rax, 0
ret
;===== End of function sayhello
When linked together with the C wrapper in the question and run in plain cmd window it works fine:
Depending on your toolchain you might need to remove the leading underscores from symbols.
I'm trying to call C function in assembler. This is my code:
C:
int multiply(int what)
{
return what * 2;
}
ASM:
extern multiply
start:
mov eax, 10
push eax
call multiply
jmp $
;empty
times 510-($-$$) db 0
dw 0xAA55
I'm compiling C code to elf by gcc (MinGW) and ASM code by NASM. I'm compiling it without any problems, but when I'm trying to use this code(for creating .bin file):
gcc -o test.bin work.o test.o
I' getting this error:
Does anybody know how to call C function from ASM code, compile it and link it to working .bin file? Please help.
Try to add '_' to multiply:
extern _multiply
Works for me in this simple example:
global _main
extern _printf
section .data
text db "291 is the best!", 10, 0
strformat db "%s", 0
section .code
_main
push dword text
push dword strformat
call _printf
add esp, 8
ret
Try "global multiply" instead of "extern multiply" in your .asm file. You shouldn't need the underscore for ELF (I don't think), but you can get Nasm to automagically add an underscore to anything "extern" or "global" by adding "--prefix _" to Nasm's command line.
Edit: I take that back, "extern" is correct. You seem not to have a "main". Try adding "--nostartfiles"
(may be only one hyphen) to gcc's command line.
Best,
Frank