I apologize in advance if my question isn't formatted in the best way, I'm new to asking questions here.
I've been interested in learning about operating system development lately, and I have run into some strange issues regarding floating point division in C. Even something as simple as 4.0f / 2.0f gives me a NaN result. I suspect this may have something to do with the compiler, however I don't know how to verify that, I would very much appreciate help in fixing this, as I've been at this for a few hours and have made little to no progress with google searches.
Github for the project, if you'd like to build it: https://github.com/AsherBearce/ToyOperatingSystem
The relevant parts of my project are as follows:
kernel/kernelmain.c:
#include "screen.h"
void main(){
enableCursor(1, 14);
clearScreen();
double a = 4.0f;
double b = 2.0f;
double c = a / b;
double ans = 2.0f;
//Division is the ONLY operation that isn't yielding the correct results, in fact c turns out to be NaN!
if (c == 2.0f){
char string[] = "Hardcoded values were correct\n\0";
print(string);
}
char out[] = "End output\0";
print(out);
while (1){
}
}
boot/bootsector.asm
org 0x7c00
bits 16
mov ax, HELLO_MSG ;Print a simple hello message :D
call _printString
xor ax, ax
;Here, we'll load the kernel into RAM
call LoadKernel
;Enter protected mode
call EnterProtMode
EnterProtMode:
cli ;Disable interrupts
lgdt [gdt_pointer] ;Load the GDT register with the start address of the GDT
mov eax, cr0
or al, 1 ;Set PE (protection enable) bit in CR0
mov cr0, eax
jmp 08h:Stage2 ;Jump to stage 2
LoadKernel:
mov bx, KERNEL_OFFSET ;Load the kernel offset into bx
mov dh, 16 ;Load 16 sectors
mov dl, [BOOT_DRIVE] ;The disk to read from
call diskload ;Load the kernel
ret
bits 32
KERNEL_OFFSET equ 0x1000
BOOT_DRIVE: db 0
Stage2:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp
;Kernel entry here
jmp KERNEL_OFFSET ;Call the kernel finally
%include 'boot/printUtils.asm'
%include 'boot/gdt.asm'
%include 'boot/diskload.asm'
HELLO_MSG: db "Booted successfully, loading kernel.", 0
times 510 - ($ - $$) db 0
dw 0xaa55
Makefile
BOOTOUTPUT = boot.bin
OSOUTPUT = os.bin
SRCS = $(shell find . -name '*.c')
CINC = $(shell find . -name '*.h')
COBJS = $(patsubst %.c, %.o, $(SRCS))
OBJDIR = build
#Final step in the build process
$(OSOUTPUT): kernel.bin $(BOOTOUTPUT)
cat $(BOOTOUTPUT) kernel.bin > $(OSOUTPUT)
#Assemble the boot sector code
$(BOOTOUTPUT): boot/bootsector.asm
nasm -f bin boot/bootsector.asm -o $(BOOTOUTPUT)
#Compile all the kernel C files
%.o:%.c $(CINC)
gcc -m32 -ffreestanding -fno-pie -fno-stack-protector -nostdlib -c $< -o $#
#Assemble the IRQ code
irq.o: kernel/irq.asm
nasm kernel/irq.asm -f elf32 -o irq.o
#Assemble the kernel entry code
kernelEntry.o: boot/kernelEntry.asm
nasm boot/kernelEntry.asm -f elf32 -o kernelEntry.o
#Link all the .o files with the kernel entry
kernel.bin: kernelEntry.o irq.o $(COBJS)
ld -melf_i386 -o kernel.bin -Ttext 0x1000 $^ --oformat binary
run:
qemu-system-x86_64 -fda $(OSOUTPUT)
clean:
rm -f *.bin *.o $(COBJS)
Edit: I've decided to include the disassembly for kernelmain.c
kernelmain.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
0: f3 0f 1e fb endbr32
4: 8d 4c 24 04 lea 0x4(%esp),%ecx
8: 83 e4 f0 and $0xfffffff0,%esp
b: ff 71 fc pushl -0x4(%ecx)
e: 55 push %ebp
f: 89 e5 mov %esp,%ebp
11: 51 push %ecx
12: 83 ec 54 sub $0x54,%esp
15: e8 fc ff ff ff call 16 <main+0x16>
1a: 83 ec 08 sub $0x8,%esp
1d: 6a 0e push $0xe
1f: 6a 01 push $0x1
21: e8 fc ff ff ff call 22 <main+0x22>
26: 83 c4 10 add $0x10,%esp
29: e8 fc ff ff ff call 2a <main+0x2a>
2e: e8 fc ff ff ff call 2f <main+0x2f>
33: dd 05 00 00 00 00 fldl 0x0
39: dd 5d f0 fstpl -0x10(%ebp)
3c: dd 05 08 00 00 00 fldl 0x8
42: dd 5d e8 fstpl -0x18(%ebp)
45: dd 45 f0 fldl -0x10(%ebp)
48: dc 75 e8 fdivl -0x18(%ebp)
4b: dd 5d e0 fstpl -0x20(%ebp)
4e: dd 05 08 00 00 00 fldl 0x8
54: dd 5d d8 fstpl -0x28(%ebp)
57: dd 45 e0 fldl -0x20(%ebp)
5a: dd 05 08 00 00 00 fldl 0x8
60: df e9 fucomip %st(1),%st
62: dd d8 fstp %st(0)
64: 7a 56 jp bc <main+0xbc>
66: dd 45 e0 fldl -0x20(%ebp)
69: dd 05 08 00 00 00 fldl 0x8
6f: df e9 fucomip %st(1),%st
71: dd d8 fstp %st(0)
73: 75 47 jne bc <main+0xbc>
75: c7 45 ac 48 61 72 64 movl $0x64726148,-0x54(%ebp)
7c: c7 45 b0 63 6f 64 65 movl $0x65646f63,-0x50(%ebp)
83: c7 45 b4 64 20 76 61 movl $0x61762064,-0x4c(%ebp)
8a: c7 45 b8 6c 75 65 73 movl $0x7365756c,-0x48(%ebp)
91: c7 45 bc 20 77 65 72 movl $0x72657720,-0x44(%ebp)
98: c7 45 c0 65 20 63 6f movl $0x6f632065,-0x40(%ebp)
9f: c7 45 c4 72 72 65 63 movl $0x63657272,-0x3c(%ebp)
a6: c7 45 c8 74 0a 00 00 movl $0xa74,-0x38(%ebp)
ad: 83 ec 0c sub $0xc,%esp
b0: 8d 45 ac lea -0x54(%ebp),%eax
b3: 50 push %eax
b4: e8 fc ff ff ff call b5 <main+0xb5>
b9: 83 c4 10 add $0x10,%esp
bc: c7 45 cc 45 6e 64 20 movl $0x20646e45,-0x34(%ebp)
c3: c7 45 d0 6f 75 74 70 movl $0x7074756f,-0x30(%ebp)
ca: c7 45 d4 75 74 00 00 movl $0x7475,-0x2c(%ebp)
d1: 83 ec 0c sub $0xc,%esp
d4: 8d 45 cc lea -0x34(%ebp),%eax
d7: 50 push %eax
d8: e8 fc ff ff ff call d9 <main+0xd9>
dd: 83 c4 10 add $0x10,%esp
e0: eb fe jmp e0 <main+0xe0>
After looking around at various resources, I found this forum page on OSdev here:
forum.osdev.org/viewtopic.php?f=1&t=21813 that described the process for first checking for an FPU, and then initializing it. Turns out that for whatever reason, my target platform does not have an FPU, which I'm guessing is the reason for the undefined behavior.
Related
I've been trying to develop a small OS and managed to switch into protected mode, in order to write C code instead of assembly, but since this means I can't use interrupt 10h anymore, I have to write chars to the video memory address. So I tried creating a new print function to easily print out whole strings instead of printing each char separately. That's where the problems came in, for some reason, while printing single chars with the printchar function works, this new print function doesn't work, no matter what I try.
Here's my C Code:
void print(char* message, int offset);
void printChar(char character, int offset);
void start() {
printChar('M', 2);
print("Test String", 4);
while (1) {
}
}
void print(char* msg, int offset) {
for (int i = 0; msg[i] != '\0'; i++)
{
printChar(msg[i], (i * 2) + offset);
}
}
void printChar(char character, int offset) {
unsigned char* vidmem = (unsigned char*)0xB8000;
*(vidmem + offset + 1) = character;
*(vidmem + offset + 2) = 0x0f;
}
I then use these commands to convert my code to binary and put it onto the second sector of a floppy disk with sectedit.
gcc -c test.c
objcopy -O binary -j .text test.o test.bin
Also here's the assembly code generated, when using objdump -d test.o
0000000000000000 <start>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 20 sub $0x20,%rsp
8: ba 02 00 00 00 mov $0x2,%edx
d: b9 4d 00 00 00 mov $0x4d,%ecx
12: e8 73 00 00 00 call 8a <printChar>
17: ba 04 00 00 00 mov $0x4,%edx
1c: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 23 <start+0x23>
23: 48 89 c1 mov %rax,%rcx
26: e8 02 00 00 00 call 2d <print>
2b: eb fe jmp 2b <start+0x2b>
000000000000002d <print>:
2d: 55 push %rbp
2e: 48 89 e5 mov %rsp,%rbp
31: 48 83 ec 30 sub $0x30,%rsp
35: 48 89 4d 10 mov %rcx,0x10(%rbp)
39: 89 55 18 mov %edx,0x18(%rbp)
3c: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
43: eb 29 jmp 6e <print+0x41>
45: 8b 45 fc mov -0x4(%rbp),%eax
48: 8d 14 00 lea (%rax,%rax,1),%edx
4b: 8b 45 18 mov 0x18(%rbp),%eax
4e: 01 c2 add %eax,%edx
50: 8b 45 fc mov -0x4(%rbp),%eax
53: 48 63 c8 movslq %eax,%rcx
56: 48 8b 45 10 mov 0x10(%rbp),%rax
5a: 48 01 c8 add %rcx,%rax
5d: 0f b6 00 movzbl (%rax),%eax
60: 0f be c0 movsbl %al,%eax
63: 89 c1 mov %eax,%ecx
65: e8 20 00 00 00 call 8a <printChar>
6a: 83 45 fc 01 addl $0x1,-0x4(%rbp)
6e: 8b 45 fc mov -0x4(%rbp),%eax
71: 48 63 d0 movslq %eax,%rdx
74: 48 8b 45 10 mov 0x10(%rbp),%rax
78: 48 01 d0 add %rdx,%rax
7b: 0f b6 00 movzbl (%rax),%eax
7e: 84 c0 test %al,%al
80: 75 c3 jne 45 <print+0x18>
82: 90 nop
83: 90 nop
84: 48 83 c4 30 add $0x30,%rsp
88: 5d pop %rbp
89: c3 ret
000000000000008a <printChar>:
8a: 55 push %rbp
8b: 48 89 e5 mov %rsp,%rbp
8e: 48 83 ec 10 sub $0x10,%rsp
92: 89 c8 mov %ecx,%eax
94: 89 55 18 mov %edx,0x18(%rbp)
97: 88 45 10 mov %al,0x10(%rbp)
9a: 48 c7 45 f8 00 80 0b movq $0xb8000,-0x8(%rbp)
a1: 00
a2: 8b 45 18 mov 0x18(%rbp),%eax
a5: 48 98 cltq
a7: 48 8d 50 01 lea 0x1(%rax),%rdx
ab: 48 8b 45 f8 mov -0x8(%rbp),%rax
af: 48 01 c2 add %rax,%rdx
b2: 0f b6 45 10 movzbl 0x10(%rbp),%eax
b6: 88 02 mov %al,(%rdx)
b8: 8b 45 18 mov 0x18(%rbp),%eax
bb: 48 98 cltq
bd: 48 8d 50 02 lea 0x2(%rax),%rdx
c1: 48 8b 45 f8 mov -0x8(%rbp),%rax
c5: 48 01 d0 add %rdx,%rax
c8: c6 00 0f movb $0xf,(%rax)
cb: 90 nop
cc: 48 83 c4 10 add $0x10,%rsp
d0: 5d pop %rbp
d1: c3 ret
d2: 90 nop
d3: 90 nop
d4: 90 nop
d5: 90 nop
d6: 90 nop
d7: 90 nop
d8: 90 nop
d9: 90 nop
da: 90 nop
db: 90 nop
dc: 90 nop
dd: 90 nop
de: 90 nop
df: 90 nop
edit: The problem basically lied in me not doing this on a linux distribution, with all the things I'd need to do to do it in Windows not properly set up, huge thanks to MichaelPetch who explained the problems to me, I've now switched to a linux VM and after slightly correcting the code, it works (as the comments pointed out my offset was weird, I used that offset as it worked in the broken setup I had, but normally it shouldn't).
I decided to compile a very basic C program and take a look at the generated code with objdump -d.
int main(int argc, char *argv[]) {
exit(0);
}
After compiling it with gcc test.c -s -o test.o and then disassembling with objdump -d my text segment looked like this:
Disassembly of section .text:
0000000000001050 <.text>:
1050: 31 ed xor %ebp,%ebp
1052: 49 89 d1 mov %rdx,%r9
1055: 5e pop %rsi
1056: 48 89 e2 mov %rsp,%rdx
1059: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
105d: 50 push %rax
105e: 54 push %rsp
105f: 4c 8d 05 4a 01 00 00 lea 0x14a(%rip),%r8 # 11b0 <__cxa_finalize#plt+0x170>
1066: 48 8d 0d e3 00 00 00 lea 0xe3(%rip),%rcx # 1150 <__cxa_finalize#plt+0x110>
106d: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1135 <__cxa_finalize#plt+0xf5>
1074: ff 15 66 2f 00 00 callq *0x2f66(%rip) # 3fe0 <__cxa_finalize#plt+0x2fa0>
107a: f4 hlt
107b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
1080: 48 8d 3d a9 2f 00 00 lea 0x2fa9(%rip),%rdi # 4030 <__cxa_finalize#plt+0x2ff0>
1087: 48 8d 05 a2 2f 00 00 lea 0x2fa2(%rip),%rax # 4030 <__cxa_finalize#plt+0x2ff0>
108e: 48 39 f8 cmp %rdi,%rax
1091: 74 15 je 10a8 <__cxa_finalize#plt+0x68>
1093: 48 8b 05 3e 2f 00 00 mov 0x2f3e(%rip),%rax # 3fd8 <__cxa_finalize#plt+0x2f98>
109a: 48 85 c0 test %rax,%rax
109d: 74 09 je 10a8 <__cxa_finalize#plt+0x68>
109f: ff e0 jmpq *%rax
10a1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10a8: c3 retq
10a9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10b0: 48 8d 3d 79 2f 00 00 lea 0x2f79(%rip),%rdi # 4030 <__cxa_finalize#plt+0x2ff0>
10b7: 48 8d 35 72 2f 00 00 lea 0x2f72(%rip),%rsi # 4030 <__cxa_finalize#plt+0x2ff0>
10be: 48 29 fe sub %rdi,%rsi
10c1: 48 c1 fe 03 sar $0x3,%rsi
10c5: 48 89 f0 mov %rsi,%rax
10c8: 48 c1 e8 3f shr $0x3f,%rax
10cc: 48 01 c6 add %rax,%rsi
10cf: 48 d1 fe sar %rsi
10d2: 74 14 je 10e8 <__cxa_finalize#plt+0xa8>
10d4: 48 8b 05 15 2f 00 00 mov 0x2f15(%rip),%rax # 3ff0 <__cxa_finalize#plt+0x2fb0>
10db: 48 85 c0 test %rax,%rax
10de: 74 08 je 10e8 <__cxa_finalize#plt+0xa8>
10e0: ff e0 jmpq *%rax
10e2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
10e8: c3 retq
10e9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10f0: 80 3d 39 2f 00 00 00 cmpb $0x0,0x2f39(%rip) # 4030 <__cxa_finalize#plt+0x2ff0>
10f7: 75 2f jne 1128 <__cxa_finalize#plt+0xe8>
10f9: 55 push %rbp
10fa: 48 83 3d f6 2e 00 00 cmpq $0x0,0x2ef6(%rip) # 3ff8 <__cxa_finalize#plt+0x2fb8>
1101: 00
1102: 48 89 e5 mov %rsp,%rbp
1105: 74 0c je 1113 <__cxa_finalize#plt+0xd3>
1107: 48 8b 3d 1a 2f 00 00 mov 0x2f1a(%rip),%rdi # 4028 <__cxa_finalize#plt+0x2fe8>
110e: e8 2d ff ff ff callq 1040 <__cxa_finalize#plt>
1113: e8 68 ff ff ff callq 1080 <__cxa_finalize#plt+0x40>
1118: c6 05 11 2f 00 00 01 movb $0x1,0x2f11(%rip) # 4030 <__cxa_finalize#plt+0x2ff0>
111f: 5d pop %rbp
1120: c3 retq
1121: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
1128: c3 retq
1129: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
1130: e9 7b ff ff ff jmpq 10b0 <__cxa_finalize#plt+0x70>
1135: 55 push %rbp
1136: 48 89 e5 mov %rsp,%rbp
1139: 48 83 ec 10 sub $0x10,%rsp
113d: 89 7d fc mov %edi,-0x4(%rbp)
1140: 48 89 75 f0 mov %rsi,-0x10(%rbp)
1144: bf 00 00 00 00 mov $0x0,%edi
1149: e8 e2 fe ff ff callq 1030 <exit#plt>
114e: 66 90 xchg %ax,%ax
1150: 41 57 push %r15
1152: 4c 8d 3d 8f 2c 00 00 lea 0x2c8f(%rip),%r15 # 3de8 <__cxa_finalize#plt+0x2da8>
1159: 41 56 push %r14
115b: 49 89 d6 mov %rdx,%r14
115e: 41 55 push %r13
1160: 49 89 f5 mov %rsi,%r13
1163: 41 54 push %r12
1165: 41 89 fc mov %edi,%r12d
1168: 55 push %rbp
1169: 48 8d 2d 80 2c 00 00 lea 0x2c80(%rip),%rbp # 3df0 <__cxa_finalize#plt+0x2db0>
1170: 53 push %rbx
1171: 4c 29 fd sub %r15,%rbp
1174: 48 83 ec 08 sub $0x8,%rsp
1178: e8 83 fe ff ff callq 1000 <exit#plt-0x30>
117d: 48 c1 fd 03 sar $0x3,%rbp
1181: 74 1b je 119e <__cxa_finalize#plt+0x15e>
1183: 31 db xor %ebx,%ebx
1185: 0f 1f 00 nopl (%rax)
1188: 4c 89 f2 mov %r14,%rdx
118b: 4c 89 ee mov %r13,%rsi
118e: 44 89 e7 mov %r12d,%edi
1191: 41 ff 14 df callq *(%r15,%rbx,8)
1195: 48 83 c3 01 add $0x1,%rbx
1199: 48 39 dd cmp %rbx,%rbp
119c: 75 ea jne 1188 <__cxa_finalize#plt+0x148>
119e: 48 83 c4 08 add $0x8,%rsp
11a2: 5b pop %rbx
11a3: 5d pop %rbp
11a4: 41 5c pop %r12
11a6: 41 5d pop %r13
11a8: 41 5e pop %r14
11aa: 41 5f pop %r15
11ac: c3 retq
11ad: 0f 1f 00 nopl (%rax)
11b0: c3 retq
As you can see, the part that was actually written by me occupies very little space.
The same program (if we ignore the fact that the main function is also treated as a function in C) in Assembly:
.global _start
.text
_start: mov $60, %rax
xor %rdi, %rdi
syscall
Assembled, linked and disassembled with gcc -c demo.s && ld demo.o -o demo && objdump -d demo:
Disassembly of section .text:
0000000000401000 <_start>:
401000: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax
401007: 48 31 ff xor %rdi,%rdi
40100a: 0f 05 syscall
The question is: what purpose do all these instructions serve and is there a way to generate code without them?
While I was writing the question I noticed that the C program calls exit() from the linked library whereas in Assembly I do it directly with a syscall. I don't think it is important in this case though.
gcc generates unnecessary (?) instructions
Yes, because you invoked GCC without asking for any compiler optimizations.
My recommendation: compile with
gcc -fverbose-asm -O2 -S test.c
then look inside the generated test.s assembler code.
BTW, most of the code is from crt0, which is given by, not emitted by, gcc. Build your executable with gcc -O2 -v test.c -o testprog to understand what GCC really does. Read documentation of GCC internals.
Since GCC is free software, you are allowed to look inside its source code and improve it. But the crt0 stuff is tricky, and operating system specific.
Consider also reading about linkers and loaders, about ELF executables, and How to write shared libraries, and the Linux Assembler HowTo.
gcc -s strips symbol names out of the final executable so you can't tell where different parts of the machine code came from.
Most of it is not from your main. To just see that, look at gcc -S output (asm source), e.g. on https://godbolt.org/. How to remove "noise" from GCC/clang assembly output?
Most of that is the CRT (C RunTime) startup code that eventually calls your main after initializing the standard library. (e.g. allocating memory for stdio buffers and so on.) It gets linked in regardless of how efficient your main is. e.g. compiling an empty int main(void){} with gcc -Os (optimize for size) will barely make it any smaller.
You could in theory compile with gcc -nostdlib and write your own _start that uses inline asm to make an exit system call.
See also
A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux
How Get arguments value using inline assembly in C without Glibc? (getting command line args complicates the exercise of writing your own _start, but the answers there show how).
C program does a lots of stuff before calling the main function. It has to initialize .data and .bss segments, set the stack, go through the constructors and destructors (yes gcc in C has a special attributes for such a functions) and initializes the library.
gcc destructor and constructor functions:
void __attribute__ ((constructor)) funcname(void);
void __attribute__ ((destructor)) funcname(void);
you may have as many constructors and destructors as you wish.
constructors are called before call to the main function, destructors on exit from the program (after the main termination)
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html#Function-Attributes
The following is my C file:
int main()
{
return 36;
}
It contains only return statement. But if I use the size command, it shows the
output like this:
mohanraj#ltsp63:~/Development/chap8$ size a.out
text data bss dec hex filename
1056 252 8 1316 524 a.out
mohanraj#ltsp63:~/Development/chap8$
Even though my program does not contain any global variable, or undeclared data. But, the output shows data segment have 252 and the bss have 8 bytes. So, why the output is like this? what is 252 and 8 refers.
Size Command
First see the definition of each column:
text - Actual machine instructions that your CPU going to execute. Linux allows to share this data.
data - All initialized variables (declarations) declared in a program (e.g., float salary=123.45;).
bss - The BSS consists of uninitialized data such as arrays that you have not set any values to or null pointers.
As Blue Moon said. On Linux, the execution starts by calling _start() function. Which does environment setup. Every C program has hidden "libraries" that depends on compilator you using. There are settings for global parameters, exit calls and after complete configuration it finally calls your main() function.
ASFAIK there's no way to see how your code looks encapsulated with configuration and _start() function. But I can show you that even your code contains more information than you thought the closer to hardware we are.
Hint:
Type readelf -a a.out to see how much information your exec really carrying.
What is inside?
Do not compare code in your source file to the size of executable file, it depends on the OS, compilator, and used libraries.
In my example, with exactly the same code, SIZE returns:
eryk#eryk-pc:~$ gcc a.c
eryk#eryk-pc:~$ size a.out
text data bss dec hex filename
1033 276 4 1313 521 a.out
Let's see what is inside...
eryk#eryk-pc:~$ gcc -S a.c
This will run the preprocessor over a.c, perform the initial compilation and then stop before the assembler is run.
eryk#eryk-pc:~$ cat a.s
.file "a.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl $36, %eax
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",#progbits
Then look on the assembly code
eryk#eryk-pc:~$ objdump -d -M intel -S a.out
a.out: file format elf32-i386
Disassembly of section .init:
08048294 <_init>:
8048294: 53 push ebx
8048295: 83 ec 08 sub esp,0x8
8048298: e8 83 00 00 00 call 8048320 <__x86.get_pc_thunk.bx>
804829d: 81 c3 63 1d 00 00 add ebx,0x1d63
80482a3: 8b 83 fc ff ff ff mov eax,DWORD PTR [ebx-0x4]
80482a9: 85 c0 test eax,eax
80482ab: 74 05 je 80482b2 <_init+0x1e>
80482ad: e8 1e 00 00 00 call 80482d0 <__gmon_start__#plt>
80482b2: 83 c4 08 add esp,0x8
80482b5: 5b pop ebx
80482b6: c3 ret
Disassembly of section .plt:
080482c0 <__gmon_start__#plt-0x10>:
80482c0: ff 35 04 a0 04 08 push DWORD PTR ds:0x804a004
80482c6: ff 25 08 a0 04 08 jmp DWORD PTR ds:0x804a008
80482cc: 00 00 add BYTE PTR [eax],al
...
080482d0 <__gmon_start__#plt>:
80482d0: ff 25 0c a0 04 08 jmp DWORD PTR ds:0x804a00c
80482d6: 68 00 00 00 00 push 0x0
80482db: e9 e0 ff ff ff jmp 80482c0 <_init+0x2c>
080482e0 <__libc_start_main#plt>:
80482e0: ff 25 10 a0 04 08 jmp DWORD PTR ds:0x804a010
80482e6: 68 08 00 00 00 push 0x8
80482eb: e9 d0 ff ff ff jmp 80482c0 <_init+0x2c>
Disassembly of section .text:
080482f0 <_start>:
80482f0: 31 ed xor ebp,ebp
80482f2: 5e pop esi
80482f3: 89 e1 mov ecx,esp
80482f5: 83 e4 f0 and esp,0xfffffff0
80482f8: 50 push eax
80482f9: 54 push esp
80482fa: 52 push edx
80482fb: 68 70 84 04 08 push 0x8048470
8048300: 68 00 84 04 08 push 0x8048400
8048305: 51 push ecx
8048306: 56 push esi
8048307: 68 ed 83 04 08 push 0x80483ed
804830c: e8 cf ff ff ff call 80482e0 <__libc_start_main#plt>
8048311: f4 hlt
8048312: 66 90 xchg ax,ax
8048314: 66 90 xchg ax,ax
8048316: 66 90 xchg ax,ax
8048318: 66 90 xchg ax,ax
804831a: 66 90 xchg ax,ax
804831c: 66 90 xchg ax,ax
804831e: 66 90 xchg ax,ax
08048320 <__x86.get_pc_thunk.bx>:
8048320: 8b 1c 24 mov ebx,DWORD PTR [esp]
8048323: c3 ret
8048324: 66 90 xchg ax,ax
8048326: 66 90 xchg ax,ax
8048328: 66 90 xchg ax,ax
804832a: 66 90 xchg ax,ax
804832c: 66 90 xchg ax,ax
804832e: 66 90 xchg ax,ax
08048330 <deregister_tm_clones>:
8048330: b8 1f a0 04 08 mov eax,0x804a01f
8048335: 2d 1c a0 04 08 sub eax,0x804a01c
804833a: 83 f8 06 cmp eax,0x6
804833d: 77 01 ja 8048340 <deregister_tm_clones+0x10>
804833f: c3 ret
8048340: b8 00 00 00 00 mov eax,0x0
8048345: 85 c0 test eax,eax
8048347: 74 f6 je 804833f <deregister_tm_clones+0xf>
8048349: 55 push ebp
804834a: 89 e5 mov ebp,esp
804834c: 83 ec 18 sub esp,0x18
804834f: c7 04 24 1c a0 04 08 mov DWORD PTR [esp],0x804a01c
8048356: ff d0 call eax
8048358: c9 leave
8048359: c3 ret
804835a: 8d b6 00 00 00 00 lea esi,[esi+0x0]
08048360 <register_tm_clones>:
8048360: b8 1c a0 04 08 mov eax,0x804a01c
8048365: 2d 1c a0 04 08 sub eax,0x804a01c
804836a: c1 f8 02 sar eax,0x2
804836d: 89 c2 mov edx,eax
804836f: c1 ea 1f shr edx,0x1f
8048372: 01 d0 add eax,edx
8048374: d1 f8 sar eax,1
8048376: 75 01 jne 8048379 <register_tm_clones+0x19>
8048378: c3 ret
8048379: ba 00 00 00 00 mov edx,0x0
804837e: 85 d2 test edx,edx
8048380: 74 f6 je 8048378 <register_tm_clones+0x18>
8048382: 55 push ebp
8048383: 89 e5 mov ebp,esp
8048385: 83 ec 18 sub esp,0x18
8048388: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
804838c: c7 04 24 1c a0 04 08 mov DWORD PTR [esp],0x804a01c
8048393: ff d2 call edx
8048395: c9 leave
8048396: c3 ret
8048397: 89 f6 mov esi,esi
8048399: 8d bc 27 00 00 00 00 lea edi,[edi+eiz*1+0x0]
080483a0 <__do_global_dtors_aux>:
80483a0: 80 3d 1c a0 04 08 00 cmp BYTE PTR ds:0x804a01c,0x0
80483a7: 75 13 jne 80483bc <__do_global_dtors_aux+0x1c>
80483a9: 55 push ebp
80483aa: 89 e5 mov ebp,esp
80483ac: 83 ec 08 sub esp,0x8
80483af: e8 7c ff ff ff call 8048330 <deregister_tm_clones>
80483b4: c6 05 1c a0 04 08 01 mov BYTE PTR ds:0x804a01c,0x1
80483bb: c9 leave
80483bc: f3 c3 repz ret
80483be: 66 90 xchg ax,ax
080483c0 <frame_dummy>:
80483c0: a1 10 9f 04 08 mov eax,ds:0x8049f10
80483c5: 85 c0 test eax,eax
80483c7: 74 1f je 80483e8 <frame_dummy+0x28>
80483c9: b8 00 00 00 00 mov eax,0x0
80483ce: 85 c0 test eax,eax
80483d0: 74 16 je 80483e8 <frame_dummy+0x28>
80483d2: 55 push ebp
80483d3: 89 e5 mov ebp,esp
80483d5: 83 ec 18 sub esp,0x18
80483d8: c7 04 24 10 9f 04 08 mov DWORD PTR [esp],0x8049f10
80483df: ff d0 call eax
80483e1: c9 leave
80483e2: e9 79 ff ff ff jmp 8048360 <register_tm_clones>
80483e7: 90 nop
80483e8: e9 73 ff ff ff jmp 8048360 <register_tm_clones>
080483ed <main>:
80483ed: 55 push ebp
80483ee: 89 e5 mov ebp,esp
80483f0: b8 24 00 00 00 mov eax,0x24
80483f5: 5d pop ebp
80483f6: c3 ret
80483f7: 66 90 xchg ax,ax
80483f9: 66 90 xchg ax,ax
80483fb: 66 90 xchg ax,ax
80483fd: 66 90 xchg ax,ax
80483ff: 90 nop
08048400 <__libc_csu_init>:
8048400: 55 push ebp
8048401: 57 push edi
8048402: 31 ff xor edi,edi
8048404: 56 push esi
8048405: 53 push ebx
8048406: e8 15 ff ff ff call 8048320 <__x86.get_pc_thunk.bx>
804840b: 81 c3 f5 1b 00 00 add ebx,0x1bf5
8048411: 83 ec 1c sub esp,0x1c
8048414: 8b 6c 24 30 mov ebp,DWORD PTR [esp+0x30]
8048418: 8d b3 0c ff ff ff lea esi,[ebx-0xf4]
804841e: e8 71 fe ff ff call 8048294 <_init>
8048423: 8d 83 08 ff ff ff lea eax,[ebx-0xf8]
8048429: 29 c6 sub esi,eax
804842b: c1 fe 02 sar esi,0x2
804842e: 85 f6 test esi,esi
8048430: 74 27 je 8048459 <__libc_csu_init+0x59>
8048432: 8d b6 00 00 00 00 lea esi,[esi+0x0]
8048438: 8b 44 24 38 mov eax,DWORD PTR [esp+0x38]
804843c: 89 2c 24 mov DWORD PTR [esp],ebp
804843f: 89 44 24 08 mov DWORD PTR [esp+0x8],eax
8048443: 8b 44 24 34 mov eax,DWORD PTR [esp+0x34]
8048447: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
804844b: ff 94 bb 08 ff ff ff call DWORD PTR [ebx+edi*4-0xf8]
8048452: 83 c7 01 add edi,0x1
8048455: 39 f7 cmp edi,esi
8048457: 75 df jne 8048438 <__libc_csu_init+0x38>
8048459: 83 c4 1c add esp,0x1c
804845c: 5b pop ebx
804845d: 5e pop esi
804845e: 5f pop edi
804845f: 5d pop ebp
8048460: c3 ret
8048461: eb 0d jmp 8048470 <__libc_csu_fini>
8048463: 90 nop
8048464: 90 nop
8048465: 90 nop
8048466: 90 nop
8048467: 90 nop
8048468: 90 nop
8048469: 90 nop
804846a: 90 nop
804846b: 90 nop
804846c: 90 nop
804846d: 90 nop
804846e: 90 nop
804846f: 90 nop
08048470 <__libc_csu_fini>:
8048470: f3 c3 repz ret
Disassembly of section .fini:
08048474 <_fini>:
8048474: 53 push ebx
8048475: 83 ec 08 sub esp,0x8
8048478: e8 a3 fe ff ff call 8048320 <__x86.get_pc_thunk.bx>
804847d: 81 c3 83 1b 00 00 add ebx,0x1b83
8048483: 83 c4 08 add esp,0x8
8048486: 5b pop ebx
8048487: c3 ret
Next step would converting above code to 01 notation.
As you can see. Even simple c program contains complicated operation the closer to hardware your code is. I hope I have explained to you why the executable file is bigger than you thought. If you have any doubts, feel free to comment my post. I will edit my answer immediately.
I am currently trying to inject code that will print helloworld into a vulnerable program. I have succeeded in injecting the code by storing it in one of the environment variables and overwriting the rip register to point to that code.
Here is my assembly and op codes:
0000000000000000 <_start>:
0: eb 17 jmp 19 <stack_setup>
0000000000000002 <execute>:
2: 48 31 c0 xor %rax,%rax
5: b0 01 mov $0x1,%al
7: 48 31 ff xor %rdi,%rdi
a: 48 ff c7 inc %rdi
d: 5e pop %rsi
e: b2 0f mov $0xf,%dl
10: 0f 05 syscall
12: b0 3c mov $0x3c,%al
14: 48 ff cf dec %rdi
17: 0f 05 syscall
0000000000000019 <stack_setup>:
19: e8 e4 ff ff ff callq 2 <execute>
1e: 48 rex.W
1f: 65 gs
20: 6c insb (%dx),%es:(%rdi)
21: 6c insb (%dx),%es:(%rdi)
22: 6f outsl %ds:(%rsi),(%dx)
23: 2c 20 sub $0x20,%al
25: 77 6f ja 96 <stack_setup+0x7d>
27: 72 6c jb 95 <stack_setup+0x7c>
29: 64 21 0a and %ecx,%fs:(%rdx)
2c: 0d .byte 0xd
The opcodes after line 19 are for the instruction db "Hello, world!", 0x0a, 0x0d. After examining the execution of the instructions in lines x2-x10 with gdb, I have noticed that it properly prepared the arguments to print the string Hello, World. However, upon executing the syscall statement, nothing happens. Same is to be said for the instructions from line x12 - x17.
I have also tried to compile and link the assembly code to execute it independently and it works properly. In addition to that, I have compiled the vulnerable program with the options
-fno-stack-protector -z execstack
So that I can run code located in the stack. I have also disabled randomizing_va_space. I want to ask if there is something I did not do to allow me to successfully execute the code I injected?
The reason might be that the write syscall returns an error code. It could be for many reasons, but maybe rdx is not all zeros, so you should xor %rdx, %rdx before loading 15 there (actually you should load 14 only, for "Hello, World!\n").
Anyway, the error from write would be returned in rax, and be a negative number (-errno), thus when you do
mov $0x3c,%al
dec %rdi
syscall
That will be an invalid syscall, and errno 38 for ENOSYS will be returned.
The exit I got to work anyway by doing
xor %rax, %rax
mov $0x3c,%al
syscall
I recommend that you run the program with strace and see from its output why exactly the write is failing.
Here's a version of shell code that works for me:
0000000000000000 <main>:
0: eb 1d jmp 1f <stack_setup>
0000000000000002 <execute>:
2: 48 31 c0 xor %rax,%rax
5: b0 01 mov $0x1,%al
7: 48 31 ff xor %rdi,%rdi
a: 48 ff c7 inc %rdi
d: 5e pop %rsi
e: 48 31 d2 xor %rdx,%rdx
11: b2 0e mov $0xe,%dl
13: 0f 05 syscall
15: 48 31 c0 xor %rax,%rax
18: b0 3c mov $0x3c,%al
1a: 48 ff cf dec %rdi
1d: 0f 05 syscall
000000000000001f <stack_setup>:
1f: e8 de ff ff ff callq 2 <execute>
24: 48 rex.W
25: 65 6c gs insb (%dx),%es:(%rdi)
27: 6c insb (%dx),%es:(%rdi)
28: 6f outsl %ds:(%rsi),(%dx)
29: 2c 20 sub $0x20,%al
2b: 57 push %rdi
2c: 6f outsl %ds:(%rsi),(%dx)
2d: 72 6c jb 9b <stack_setup+0x7c>
2f: 64 21 0a and %ecx,%fs:(%rdx)
I wrote a very simple C code as follow:
int data_items[] = {3,67,32,4,89,6,34,2,9,0};
int max(int* pt)
{
int val = *pt;
while(*pt != 0)
{
if (*pt > val)
{
val = *pt;
}
++pt;
}
return val;
}
int main()
{
max(data_items);
return 0;
}
then I compiled it with gcc:
gcc main.c
and then disassembled it with:
objdump -d a.out
At last I get the assemble code:
a.out: file format elf64-x86-64
Disassembly of section .init:
00000000004003a8 <_init>:
4003a8: 48 83 ec 08 sub $0x8,%rsp
4003ac: 48 8b 05 45 0c 20 00 mov 0x200c45(%rip),%rax # 600ff8 <_DYNAMIC+0x1d0>
4003b3: 48 85 c0 test %rax,%rax
4003b6: 74 05 je 4003bd <_init+0x15>
4003b8: e8 33 00 00 00 callq 4003f0 <__gmon_start__#plt>
4003bd: 48 83 c4 08 add $0x8,%rsp
4003c1: c3 retq
Disassembly of section .plt:
00000000004003d0 <__libc_start_main#plt-0x10>:
4003d0: ff 35 32 0c 20 00 pushq 0x200c32(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
4003d6: ff 25 34 0c 20 00 jmpq *0x200c34(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
4003dc: 0f 1f 40 00 nopl 0x0(%rax)
00000000004003e0 <__libc_start_main#plt>:
4003e0: ff 25 32 0c 20 00 jmpq *0x200c32(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
4003e6: 68 00 00 00 00 pushq $0x0
4003eb: e9 e0 ff ff ff jmpq 4003d0 <_init+0x28>
00000000004003f0 <__gmon_start__#plt>:
4003f0: ff 25 2a 0c 20 00 jmpq *0x200c2a(%rip) # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
4003f6: 68 01 00 00 00 pushq $0x1
4003fb: e9 d0 ff ff ff jmpq 4003d0 <_init+0x28>
Disassembly of section .text:
0000000000400400 <_start>:
400400: 31 ed xor %ebp,%ebp
400402: 49 89 d1 mov %rdx,%r9
400405: 5e pop %rsi
400406: 48 89 e2 mov %rsp,%rdx
400409: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40040d: 50 push %rax
40040e: 54 push %rsp
40040f: 49 c7 c0 b0 05 40 00 mov $0x4005b0,%r8
400416: 48 c7 c1 40 05 40 00 mov $0x400540,%rcx
40041d: 48 c7 c7 28 05 40 00 mov $0x400528,%rdi
400424: e8 b7 ff ff ff callq 4003e0 <__libc_start_main#plt>
400429: f4 hlt
40042a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
0000000000400430 <deregister_tm_clones>:
400430: b8 8f 10 60 00 mov $0x60108f,%eax
400435: 55 push %rbp
400436: 48 2d 88 10 60 00 sub $0x601088,%rax
40043c: 48 83 f8 0e cmp $0xe,%rax
400440: 48 89 e5 mov %rsp,%rbp
400443: 77 02 ja 400447 <deregister_tm_clones+0x17>
400445: 5d pop %rbp
400446: c3 retq
400447: b8 00 00 00 00 mov $0x0,%eax
40044c: 48 85 c0 test %rax,%rax
40044f: 74 f4 je 400445 <deregister_tm_clones+0x15>
400451: 5d pop %rbp
400452: bf 88 10 60 00 mov $0x601088,%edi
400457: ff e0 jmpq *%rax
400459: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000400460 <register_tm_clones>:
400460: b8 88 10 60 00 mov $0x601088,%eax
400465: 55 push %rbp
400466: 48 2d 88 10 60 00 sub $0x601088,%rax
40046c: 48 c1 f8 03 sar $0x3,%rax
400470: 48 89 e5 mov %rsp,%rbp
400473: 48 89 c2 mov %rax,%rdx
400476: 48 c1 ea 3f shr $0x3f,%rdx
40047a: 48 01 d0 add %rdx,%rax
40047d: 48 d1 f8 sar %rax
400480: 75 02 jne 400484 <register_tm_clones+0x24>
400482: 5d pop %rbp
400483: c3 retq
400484: ba 00 00 00 00 mov $0x0,%edx
400489: 48 85 d2 test %rdx,%rdx
40048c: 74 f4 je 400482 <register_tm_clones+0x22>
40048e: 5d pop %rbp
40048f: 48 89 c6 mov %rax,%rsi
400492: bf 88 10 60 00 mov $0x601088,%edi
400497: ff e2 jmpq *%rdx
400499: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000004004a0 <__do_global_dtors_aux>:
4004a0: 80 3d e1 0b 20 00 00 cmpb $0x0,0x200be1(%rip) # 601088 <__TMC_END__>
4004a7: 75 11 jne 4004ba <__do_global_dtors_aux+0x1a>
4004a9: 55 push %rbp
4004aa: 48 89 e5 mov %rsp,%rbp
4004ad: e8 7e ff ff ff callq 400430 <deregister_tm_clones>
4004b2: 5d pop %rbp
4004b3: c6 05 ce 0b 20 00 01 movb $0x1,0x200bce(%rip) # 601088 <__TMC_END__>
4004ba: f3 c3 repz retq
4004bc: 0f 1f 40 00 nopl 0x0(%rax)
00000000004004c0 <frame_dummy>:
4004c0: 48 83 3d 58 09 20 00 cmpq $0x0,0x200958(%rip) # 600e20 <__JCR_END__>
4004c7: 00
4004c8: 74 1e je 4004e8 <frame_dummy+0x28>
4004ca: b8 00 00 00 00 mov $0x0,%eax
4004cf: 48 85 c0 test %rax,%rax
4004d2: 74 14 je 4004e8 <frame_dummy+0x28>
4004d4: 55 push %rbp
4004d5: bf 20 0e 60 00 mov $0x600e20,%edi
4004da: 48 89 e5 mov %rsp,%rbp
4004dd: ff d0 callq *%rax
4004df: 5d pop %rbp
4004e0: e9 7b ff ff ff jmpq 400460 <register_tm_clones>
4004e5: 0f 1f 00 nopl (%rax)
4004e8: e9 73 ff ff ff jmpq 400460 <register_tm_clones>
00000000004004ed <max>:
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 48 89 7d e8 mov %rdi,-0x18(%rbp)
4004f5: 48 8b 45 e8 mov -0x18(%rbp),%rax
4004f9: 8b 00 mov (%rax),%eax
4004fb: 89 45 fc mov %eax,-0x4(%rbp)
4004fe: eb 19 jmp 400519 <max+0x2c>
400500: 48 8b 45 e8 mov -0x18(%rbp),%rax
400504: 8b 00 mov (%rax),%eax
400506: 3b 45 fc cmp -0x4(%rbp),%eax
400509: 7e 09 jle 400514 <max+0x27>
40050b: 48 8b 45 e8 mov -0x18(%rbp),%rax
40050f: 8b 00 mov (%rax),%eax
400511: 89 45 fc mov %eax,-0x4(%rbp)
400514: 48 83 45 e8 04 addq $0x4,-0x18(%rbp)
400519: 48 8b 45 e8 mov -0x18(%rbp),%rax
40051d: 8b 00 mov (%rax),%eax
40051f: 85 c0 test %eax,%eax
400521: 75 dd jne 400500 <max+0x13>
400523: 8b 45 fc mov -0x4(%rbp),%eax
400526: 5d pop %rbp
400527: c3 retq
0000000000400528 <main>:
400528: 55 push %rbp
400529: 48 89 e5 mov %rsp,%rbp
40052c: bf 60 10 60 00 mov $0x601060,%edi
400531: e8 b7 ff ff ff callq 4004ed <max>
400536: b8 00 00 00 00 mov $0x0,%eax
40053b: 5d pop %rbp
40053c: c3 retq
40053d: 0f 1f 00 nopl (%rax)
0000000000400540 <__libc_csu_init>:
400540: 41 57 push %r15
400542: 41 89 ff mov %edi,%r15d
400545: 41 56 push %r14
400547: 49 89 f6 mov %rsi,%r14
40054a: 41 55 push %r13
40054c: 49 89 d5 mov %rdx,%r13
40054f: 41 54 push %r12
400551: 4c 8d 25 b8 08 20 00 lea 0x2008b8(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>
400558: 55 push %rbp
400559: 48 8d 2d b8 08 20 00 lea 0x2008b8(%rip),%rbp # 600e18 <__init_array_end>
400560: 53 push %rbx
400561: 4c 29 e5 sub %r12,%rbp
400564: 31 db xor %ebx,%ebx
400566: 48 c1 fd 03 sar $0x3,%rbp
40056a: 48 83 ec 08 sub $0x8,%rsp
40056e: e8 35 fe ff ff callq 4003a8 <_init>
400573: 48 85 ed test %rbp,%rbp
400576: 74 1e je 400596 <__libc_csu_init+0x56>
400578: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40057f: 00
400580: 4c 89 ea mov %r13,%rdx
400583: 4c 89 f6 mov %r14,%rsi
400586: 44 89 ff mov %r15d,%edi
400589: 41 ff 14 dc callq *(%r12,%rbx,8)
40058d: 48 83 c3 01 add $0x1,%rbx
400591: 48 39 eb cmp %rbp,%rbx
400594: 75 ea jne 400580 <__libc_csu_init+0x40>
400596: 48 83 c4 08 add $0x8,%rsp
40059a: 5b pop %rbx
40059b: 5d pop %rbp
40059c: 41 5c pop %r12
40059e: 41 5d pop %r13
4005a0: 41 5e pop %r14
4005a2: 41 5f pop %r15
4005a4: c3 retq
4005a5: 66 66 2e 0f 1f 84 00 data32 nopw %cs:0x0(%rax,%rax,1)
4005ac: 00 00 00 00
00000000004005b0 <__libc_csu_fini>:
4005b0: f3 c3 repz retq
Disassembly of section .fini:
00000000004005b4 <_fini>:
4005b4: 48 83 ec 08 sub $0x8,%rsp
4005b8: 48 83 c4 08 add $0x8,%rsp
4005bc: c3 retq
My question is that, in main function:
40052c: bf 60 10 60 00 mov $0x601060,%edi
the address $0x601060 should be the address of the array data_items in the C code. But I can not find the data_items array value in the assemble code, where is it?
By the way, in the max function code:
4004f1: 48 89 7d e8 mov %rdi,-0x18(%rbp)
I do think the stack should be:
mov %rdi, -0x8(%rbp)
why the compiler makes some hole in the stack?
my system is Ubuntu 14.04 LTS.
objdump -d only disassembles sections of the executable where there's supposed to be code.
Use objdump -D to disassemble all sections, and you'll find this:
Disassembly of section .data:
08049760 <__data_start>:
...
08049780 <data_items>:
8049780: 03 00 add (%eax),%eax
8049782: 00 00 add %al,(%eax)
8049784: 43 inc %ebx
8049785: 00 00 add %al,(%eax)
8049787: 00 20 add %ah,(%eax)
8049789: 00 00 add %al,(%eax)
804978b: 00 04 00 add %al,(%eax,%eax,1)
804978e: 00 00 add %al,(%eax)
That's your array. objdump will try to disassemble it as if it was machine code, so the assembly isn't going to make sense though).
If you run nm a.out, you will see that 0x601060 is the address of data_items.
When it comes to runtime dissasembly gdb itself can be very handy:
(gdb) break main
Breakpoint 1 at 0x4004f3
(gdb) break max
Breakpoint 2 at 0x4004b8
(gdb) run
Starting program: /home/dtarcatu/workspace/ctest/a.out
Breakpoint 1, 0x00000000004004f3 in main ()
(gdb) disas
Dump of assembler code for function main:
0x00000000004004ef <+0>: push %rbp
0x00000000004004f0 <+1>: mov %rsp,%rbp
=> 0x00000000004004f3 <+4>: mov $0x601040,%edi
0x00000000004004f8 <+9>: callq 0x4004b4 <max>
0x00000000004004fd <+14>: mov $0x0,%eax
0x0000000000400502 <+19>: pop %rbp
0x0000000000400503 <+20>: retq
End of assembler dump.
(gdb) x 0x601040
0x601040 <data_items>: 0x00000003
(gdb) x /10d 0x601040
0x601040 <data_items>: 3 67 32 4
0x601050 <data_items+16>: 89 6 34 2
0x601060 <data_items+32>: 9 0
Now regarding your stack discipline dillema I'm not sure I can come up with a very good answer, but I think it's related to the x86-64 red zone optimization.
(gdb) c
Continuing.
Breakpoint 2, 0x00000000004004b8 in max ()
(gdb) disas
Dump of assembler code for function max:
0x00000000004004b4 <+0>: push %rbp
0x00000000004004b5 <+1>: mov %rsp,%rbp
=> 0x00000000004004b8 <+4>: mov %rdi,-0x18(%rbp)
0x00000000004004bc <+8>: mov -0x18(%rbp),%rax
0x00000000004004c0 <+12>: mov (%rax),%eax
0x00000000004004c2 <+14>: mov %eax,-0x4(%rbp)
0x00000000004004c5 <+17>: jmp 0x4004e0 <max+44>
0x00000000004004c7 <+19>: mov -0x18(%rbp),%rax
0x00000000004004cb <+23>: mov (%rax),%eax
0x00000000004004cd <+25>: cmp -0x4(%rbp),%eax
0x00000000004004d0 <+28>: jle 0x4004db <max+39>
0x00000000004004d2 <+30>: mov -0x18(%rbp),%rax
0x00000000004004d6 <+34>: mov (%rax),%eax
0x00000000004004d8 <+36>: mov %eax,-0x4(%rbp)
0x00000000004004db <+39>: addq $0x4,-0x18(%rbp)
0x00000000004004e0 <+44>: mov -0x18(%rbp),%rax
0x00000000004004e4 <+48>: mov (%rax),%eax
0x00000000004004e6 <+50>: test %eax,%eax
0x00000000004004e8 <+52>: jne 0x4004c7 <max+19>
0x00000000004004ea <+54>: mov -0x4(%rbp),%eax
0x00000000004004ed <+57>: pop %rbp
0x00000000004004ee <+58>: retq
End of assembler dump.
(gdb) ni
0x00000000004004bc in max ()
(gdb) print $rbp
$1 = (void *) 0x7fffffffdf10
(gdb) print $rsp
$2 = (void *) 0x7fffffffdf10
The -0x18(%rbp) location is definitely inside the red zone. I'm not sure how the compiler uses that but it can probably do whatever the hack it wants in there. Your local int seems to be stored at -0x4(%rbp) later on, so I guess -0x18(%rbp) is just some sort of temporary buffer.
You could also ask the compiler to output its emitted assembler code. Notice that there is no observable side-effect in your program, so the compiler could optimize it to a nop -or compute the max at compile-time, etc.
However,compiling your code with (GCC 4.9.1 Debian/x86-64)
gcc -fverbose-asm -O -S main.c
I'm getting a file main.s containing (with several lines before and after) :
.globl max
.type max, #function
max:
.LFB0:
.file 1 "main.c"
.loc 1 4 0
.cfi_startproc
.LVL0:
.loc 1 5 0
movl (%rdi), %eax # *pt_5(D), val
.LVL1:
.loc 1 6 0
testl %eax, %eax # val
je .L2 #,
movl %eax, %edx # val, val
.L3:
cmpl %edx, %eax # val, val
cmovl %edx, %eax # val,, val, val
.LVL2:
.loc 1 12 0
addq $4, %rdi #, pt
.LVL3:
.loc 1 6 0
movl (%rdi), %edx # MEM[base: pt_8, offset: 0B], val
testl %edx, %edx # val
jne .L3 #,
.L2:
.loc 1 15 0
rep ret
.cfi_endproc
.LFE0:
.size max, .-max
.globl main
.type main, #function
main:
.LFB1:
.loc 1 18 0
.cfi_startproc
.loc 1 20 0
movl $data_items, %edi #,
call max #
.LVL4:
.loc 1 23 0
movl $0, %eax #,
ret
.cfi_endproc
.LFE1:
.size main, .-main
.globl data_items
.data
.align 32
.type data_items, #object
.size data_items, 40
data_items:
.long 3
.long 67
.long 32
.long 4
.long 89
.long 6
.long 34
.long 2
.long 9
.long 0
.text
So you see that data_items goes into the data section (because of the .data directive)
BTW, GCC usually emits an assembler file transformed by the as assembler into an ELF file main.o containing the object code. That object file contains relocation directives and several sections which are later processed by the ld linker.