Page 1 of 3
Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 3:33 am
by scippie
Ok, this is really strange. I'm a good C and C++ programmer, so I really don't get this and I must be overlooking something very VERY stupid.
This is the code (64-bit kernel code started from my own bootloader in asm):
Code: Select all
unsigned long long dx = 0xdeadcafebabebeaf;
void printhex(unsigned long long v);
void main(void)
{
printhex((unsigned long long)&dx);
__asm__("hlt");
}
void printhex(unsigned long long v)
{
char buf[16];
for (int i = 0; i < 16; ++i)
{
int r = v & 0xf;
if (r < 10)
buf[15 - i] = '0' + r;
else
buf[15 - i] = 'A' + r - 10;
v >>= 4;
}
char *video = (char*)0xB8000;
bool showzeroes = false;
for (int i = 0; i < 16; ++i)
{
if (showzeroes || buf[i] != '0')
{
*video++ = buf[i];
*video++ = 0x07;
showzeroes = true;
}
}
}
Linker script:
Code: Select all
ENTRY(_start);
SECTIONS {
_start = 0x1a00 ;
.text : ALIGN(0x1a00) {
_TEXT_START_ = .;
*(.text)
_TEXT_END_ = .;
}
.data : ALIGN(0x1a00) {
_DATA_START_ = .;
*(.data)
_DATA_END_ = .;
}
.bss : ALIGN(0x1a00) {
_BSS_START_ = .;
*(.bss)
_BSS_END_ = .;
}
}
Code is started correctly, variable dx is located at address 0x2600 (checked in the resulting disk image file). Although I think the linker should put the data directly after the code, this is acceptable.
In my opinion, the output of this on screen should be something like: 3400
But the output is: DEADCAFEBABEBEAF !!! which is the value in the variable...
I would expect that to be the result when the code would be:
But when I execute that code, I get an exception!??
This is so weird... Am I doing something so completely wrong here?
Must be something stupid of course, but I can't see it...
I'm so confused!
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 3:48 am
by Combuster
64-bit kernel code started from my own bootloader in asm
What could possibly go wrong?
I can see there's already two things missing from your linker script (a missing section, output format) that make it fail sanity checks, a bunch of arbitrary-looking magic numbers, and there are possibly more specific error causes to point out in all the material that hasn't been posted.
My crystal ball says that it's not the instructions from main() that get executed, but something close...
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 3:52 am
by scippie
Combuster wrote:64-bit kernel code started from my own bootloader in asm
What could possibly go wrong?
I can see there's already two things missing from your linker script (a missing section, output format) that make it fail sanity checks, a bunch of arbitrary-looking magic numbers, and there are possibly more specific error causes to point out in all the material that hasn't been posted.
My crystal ball says that it's not the instructions from main() that get executed, but something close...
Ah, but my previous test-code without global variables executed perfectly. But you are probably right, this looks like it indeed.
Output format and stuff like that are set in the make file:
Code: Select all
AS=/usr/local/cross/bin/fasm -m 265536
CC=/usr/local/cross/bin/x86_64-elf-gcc
CFLAGS=-Wall -m64 -fomit-frame-pointer -fno-builtin -ffreestanding -fPIC -std=c99 -c -save-temps
all: disk.bin
boot.bin:
$(AS) boot.asm
flatmode.bin:
$(AS) flatmode.asm
longmode.bin:
$(AS) longmode.asm
kernel.bin:
$(CC) $(CFLAGS) kernel.c -o kernel.o
$(LD) --format elf64-x86-64 --oformat binary --warn-constructors --warn-common -static -Bsymbolic -T os.lds kernel.o -o kernel.bin
disk.bin: boot.bin flatmode.bin longmode.bin kernel.bin
$(AS) disk.asm
clean:
rm *.bin
The generated assembler code on the &dx is:
Code: Select all
.file "kernel.c"
.globl dx
.data
.align 8
.type dx, @object
.size dx, 8
dx:
.quad -2401039830915039569
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movq dx@GOTPCREL(%rip), %rax
movq %rax, %rdi
call printhex@PLT
/APP
# 31 "kernel.c" 1
hlt
# 0 "" 2
...
So what does your crystal ball say now?
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 4:11 am
by bluemoon
I see no fatal problem on the posted code, but as Combuster pointed out, make sure the main is actually get executed, not by the generated assembly but check with the dis-assembly code from the real binary, and furthermore with debugger.
by the way, unsigned long long is terrible term by Microsoft compilers, if you need 64-bit integer you can use uint64_t or its family.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 4:20 am
by Combuster
(ninja'd by bluemoon)
scippie wrote:So what does your crystal ball say now?
"Clouded by one subject, the mind is. Looking elsewhere, it has not."
@bluemoon: where'd the typedef for uint64_t go? (and in particular, on x86-32
)
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 4:58 am
by bluemoon
On gcc, you can use uint64_t even on 32-bit compiler, just include from stdint.h
It however require linking to libgcc.a for 64-bit arithmetic to work.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 5:23 am
by scippie
bluemoon wrote:I see no fatal problem on the posted code, but as Combuster pointed out, make sure the main is actually get executed, not by the generated assembly but check with the dis-assembly code from the real binary, and furthermore with debugger.
Can you tell me how I can disassemble a flat binary? I can't get it to work...
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 5:26 am
by bluemoon
I never used flat binary for anything except some crappy designed plugin for DOS program ten years ago, so I can't answer you.
I suggest you switch to ELF, having tons of tools and reference it will make your life much easier.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 5:41 am
by Griwes
bluemoon wrote:I see no fatal problem on the posted code, but as Combuster pointed out, make sure the main is actually get executed, not by the generated assembly but check with the dis-assembly code from the real binary, and furthermore with debugger.
by the way, unsigned long long is terrible term by Microsoft compilers, if you need 64-bit integer you can use uint64_t or its family.
`unsigned long long` and `long long` are standard C99 and C++11 types, defined to be at least 64-bit (that is, it is always safe to do `typedef long long int_least64_t;`.
(I do agree that one should use fixed length typedefs; I just don't get what you had in mind by saying long long is "terrible term by Microsoft compilers". It standard since 14 years in C, 2 years in C++, and since a long time as a GCC extension - dunno about MS compilers here, but it is, by no means, a term by MS compilers).
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 5:45 am
by Combuster
scippie wrote:Can you tell me how I can disassemble a flat binary? I can't get it to work...
The method you
need to know right now is the "u <address>" command in bochs' debugger. Although it already prints the next instruction whenever it pauses from emulating so you might not even need that.
For the flat answer...
you know...
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 5:47 am
by dozniak
scippie wrote:
Code: Select all
bool showzeroes = false;
for (int i = 0; i < 16; ++i)
{
if (showzeroes || buf[i] != '0')
{
*video++ = buf[i];
*video++ = 0x07;
showzeroes = true;
Just a side note: if you try to print value of v 0, it will print nothing at all.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 6:33 am
by scippie
Really... google is so new to me. So googling was what I had done a couple of days ago and I found the same result and it did not work for me:
$ objdump -b binary --adjust-vma=0x1a00 -D kernel.bin
kernel.bin: file format binary
/usr/local/cross/x86_64-elf/bin/objdump: can't disassemble for architecture UNKNOWN!
So I tried passing the architecture but that didn't help either.
dozniak wrote:Just a side note: if you try to print value of v 0, it will print nothing at all.
I know.
Bochs, ELF, all things I don't use and actually want to prevent using. Using C and actually using linux (or anything else) for creating this code is against what I want to achieve, but ok, one does need something to get started. Also I don't care if I use uint64_t or unsigned long long. I would actually prefer qword anyway.
But all of these are not really related to my question. I have debugged a bit further and I discovered that even with the global variable in place, the code still seems to be called at the correct address! When I put this line of code at the beginning: *((int*)0xb8000 + 320) = 124323656; it actually works. This means IMHO that main is still started at the correct address and thus there is still some kind of misunderstanding of my C code which is of course not possible, I know that too.
But I believe this must be a problem that starts earlier, so I will continue my debugging and I hope I will find out what it is. Once I get started with C, I can continue creating the greatest OS ever.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 7:07 am
by bluemoon
Just make sure you're not trying to prevent using things for the wrong reason, I've seen too many of these. I assume you have some good reasons.
Note) One common mistake is that thinking something is inconvenient or complex to deal with, and trying to avoid them, while not aware that such thing could ease the life afterward.
By the way, while not directly solving the problem, people (include myself) point out other potential issue (print 0) or useful stuff (debugger, bochs, elf, the way to tackle the problem) and you could appreciated them as a bonus, instead of I don't care.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 7:08 am
by iansjack
Run your code in qemu with debugging enabled. Use gdb and single-step throught the code. The problem should then reveal itself.
Re: Pointer values C not interpreted correctly?
Posted: Tue Feb 05, 2013 7:12 am
by bluemoon
scippie wrote:I have debugged a bit further and I discovered that even with the global variable in place, the code still seems to be called at the correct address! When I put this line of code at the beginning: *((int*)0xb8000 + 320) = 124323656; it actually works. This means IMHO that main is still started at the correct address and thus there is still some kind of misunderstanding of my C code which is of course not possible, I know that too
No. Another possibility is that there is misalignment(or disagreement) of the location of code for compiler and the running machine. By padding code you may get different executing path and thus generating such observable result. But I won't bet on guessing, have you actually see what's executing on the debugger?