Related
I have one file-level static C variable that isn't getting initialized.
const size_t VGA_WIDTH = 80;
const size_t VGA_HEIGHT = 25;
static uint16_t* vgat_buffer = (uint16_t*)0x62414756; // VGAb
static char vgat_initialized= '\0';
In particular, vgat_initialized isn't always 0 the first time it is accessed. (Of course, the problem only appears on certain machines.)
I'm playing around with writing my own OS, so I'm pretty sure this is a problem with my linker script; but, I'm not clear how exactly the variables are supposed to be organized in the image produced by the linker (i.e., I'm not sure if this variable is supposed to go in .data, .bss, some other section, etc.)
VGA_WIDTH and VGA_HEIGHT get placed in the .rodata section as expected.
vgat_buffer is placed in the .data section, as expected (By initializing this variable to 0x62417656, I can clearly see where the linker places it in the resulting image file.)
I can't figure out where vgat_initialized is supposed to go. I've included the relevant parts of the assembly file below. From what I understand, the .comm directive is supposed to allocate space for the variable in the data section; but, I can't tell where. Looking in the linker's map file didn't provide any clues either.
Interestingly enough, if I change the initialization to
static char vgat_initialized= 'x';
everything works as expected: I can clearly see where the variable is placed in the resulting image file (i.e., I can see the x in the hexdump of the image file).
Assembly code generated from the C file:
.text
.LHOTE15:
.local buffer.1138
.comm buffer.1138,100,64
.local buffer.1125
.comm buffer.1125,100,64
.local vgat_initialized
.comm vgat_initialized,1,1
.data
.align 4
.type vgat_buffer, #object
.size vgat_buffer, 4
vgat_buffer:
.long 1648445270
.globl VGA_HEIGHT
.section .rodata
.align 4
.type VGA_HEIGHT, #object
.size VGA_HEIGHT, 4
VGA_HEIGHT:
.long 25
.globl VGA_WIDTH
.align 4
.type VGA_WIDTH, #object
.size VGA_WIDTH, 4
VGA_WIDTH:
.long 80
.ident "GCC: (GNU) 4.9.2"
compilers can conform to their own names for sections certainly but using the common .data, .text, .rodata, .bss that we know from specific compilers, this should land in .bss.
But that doesnt in any way automatically zero it out. There needs to be a mechanism, sometimes depending on your toolchain the toolchain takes care of it and creates a binary that in addition to .data, .rodata (and naturally .text) being filled in will fill in .bss in the binary. But depends on a few things, primarily is this a simple ram only image, is everything living under one memory space definition in the linker script.
you could for example put .data after .bss in the linker script and depending the binary format you use and/or tools that convert that you could end up with zeroed memory in the binary without any other work.
Normally though you should expect to using toolchain specific (linker scripts are linker specific not to be assumed to be universal to all tools) mechanism for defining where .bss is from your perspective, then some form of communication from the linker as to where it starts and what size, that information is used by the bootstrap whose job it is to zero it in that case, and one can assume it is always the bootstrap's job to zero .bss with naturally some exceptions. Likewise if the binary is meant to be on a read only media (rom, flash, etc) but .data, and .bss are read/write you need to have .data in its entirety on this media then someone has to copy it to its runtime position in ram, and .bss is either part of that depending on the toolchain and how you used it or the start address and size are on the read only media and someone has to zero that space at some point pre-main(). Here again this is the job of the bootstrap. Set the stack pointer, move .data if needed, zero .bss are the typical minimal jobs of the bootstrap, you can shortcut them in special cases or avoid using .data or .bss.
Since it is the linkers job to take all the little .data and .bss (and other) definitions from the objects being linked and combine them per the directions from the user (linker script, command line, whatever that tool uses), the linker ultimately knows.
In the case of gcc you use what I would call variables that are defined in the linker script, the linker script can fill in these values with matching variable/label names for the assembler such that a generic bootstrap can be used and you dont have to do any more work than that.
Like this but possibly more complicated
MEMORY
{
bob : ORIGIN = 0x8000, LENGTH = 0x1000
ted : ORIGIN = 0xA000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > bob
__data_rom_start__ = .;
.data : {
__data_start__ = .;
*(.data*)
} > ted AT > bob
__data_end__ = .;
__data_size__ = __data_end__ - __data_start__;
.bss : {
__bss_start__ = .;
*(.bss*)
} > bob
__bss_end__ = .;
__bss_size__ = __bss_end__ - __bss_start__;
}
then you can pull these into the assembly language bootstrap
.globl bss_start
bss_start: .word __bss_start__
.globl bss_end
bss_end: .word __bss_end__
.word __bss_size__
.globl data_rom_start
data_rom_start:
.word __data_rom_start__
.globl data_start
data_start:
.word __data_start__
.globl data_end
data_end:
.word __data_end__
.word __data_size__
and then write some code to operate on those as needed for your design.
you can simply put things like that in a linked in assembly language file without other code using them and assemble, compile other code and link and then the disassembly or other tools you prefer will show you what the linker generated, tweak that until you are satisfied then you can write or borrow or steal bootstrap code to use them.
for bare metal I prefer to not completely conform to the standard with my code, dont have any .data and dont expect .bss to be zero, so my bootstrap sets the stack pointer and calls main, done. For an operating system, you should conform. the toolchains already have this solved for the native platform, but if you are taking over that with your own linker script and boostrap then you need to deal with it, if you want to use an existing toolchains solution for an existing operating system then...done...just do that.
This answer is simply an extension of the others. As has been mentioned C standard has rules about initialization:
10) If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static storage duration is not initialized explicitly, then:
if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
if it is an aggregate, every member is initialized (recursively) according to these rules;
if it is a union, the first named member is initialized (recursively) according to these rules.
The problem in your code is that a computers memory may not always be initialized to zero. It is up to you to make sure the BSS section is initialized to zero in a free standing environment (like your OS and bootloader).
The BSS sections usually don't (by default) take up space in a binary file and usually occupy memory in the area beyond the limits of the code and data that appears in the binary. This is done to reduce the size of the binary that has to be read into memory.
I know you are writing an OS for x86 booting with legacy BIOS. I know that you are using GCC from your other recent questions. I know you are using GNU assembler for part of your bootloader. I know that you have a linker script, but I don't know what it looks like. The usual mechanism to do this is via a linker script that places the BSS data at the end, and creates start and end symbols to define the address extents of the section. Once these symbols are defined by the linker they can be used by C code (or assembly code) to loop through the region and set it to zero.
I present a reasonably simple MCVE that does this. The code reads an extra sector with the kernel with Int 13h/AH=2h; enables the A20 line (using fast A20 method); loads a GDT with 32-bit descriptors; enables protected mode; completes the transition into 32-bit protected mode; and then calls a kernel entry point in C called kmain. kmain calls a C function called zero_bss that initializes the BSS section based on the starting and ending symbols (__bss_start and __bss_end) generated by a custom linker script.
boot.S:
.extern kmain
.globl mbrentry
.code16
.section .text
mbrentry:
# If trying to create USB media, a BPB here may be needed
# At entry DL contains boot drive number
# Segment registers to zero
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
# Set stack to grow down from area under the place the bootloader was loaded
mov %ax, %ss
mov $0x7c00, %sp
cld # Ensure forward direction of MOVS/SCAS/LODS instructions
# which is required by generated C code
# Load kernel into memory
mov $0x02, %ah # Disk read
mov $1, %al # Read 1 sector
xor %ch, %ch # Cylinder 0
xor %dh, %dh # Head 0
mov $2, %cl # Start reading from second sector
mov $0x7e00, %bx # Load kernel at 0x7e00
int $0x13
# Quick and dirty A20 enabling. May not work on all hardware
a20fast:
in $0x92, %al
or $2, %al
out %al, $0x92
loadgdt:
cli # Turn off interrupts until a Interrupt Vector
# Table (IVT) is set
lgdt (gdtr)
mov %cr0, %eax
or $1, %al
mov %eax, %cr0 # Enable protected mode
jmp $0x08,$init_pm # FAR JMP to next instruction to set
# CS selector with a 32-bit code descriptor and to
# flush the instruction prefetch queue
.code32
init_pm:
# Set remaining 32-bit selectors
mov $DATA_SEG, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
# Start executing kernel
call kmain
cli
loopend: # Infinite loop when finished
hlt
jmp loopend
.align 8
gdt_start:
.long 0 # null descriptor
.long 0
gdt_code:
.word 0xFFFF # limit low
.word 0 # base low
.byte 0 # base middle
.byte 0b10011010 # access
.byte 0b11001111 # granularity/limit high
.byte 0 # base high
gdt_data:
.word 0xFFFF # limit low (Same as code)
.word 0 # base low
.byte 0 # base middle
.byte 0b10010010 # access
.byte 0b11001111 # granularity/limit high
.byte 0 # base high
end_of_gdt:
gdtr:
.word end_of_gdt - gdt_start - 1
# limit (Size of GDT)
.long gdt_start # base of GDT
CODE_SEG = gdt_code - gdt_start
DATA_SEG = gdt_data - gdt_start
kernel.c:
#include <stdint.h>
extern uintptr_t __bss_start[];
extern uintptr_t __bss_end[];
/* Zero the BSS section 4-bytes at a time */
static void zero_bss(void)
{
uint32_t *memloc = __bss_start;
while (memloc < __bss_end)
*memloc++ = 0;
}
int kmain(){
zero_bss();
return 0;
}
link.ld
ENTRY(mbrentry)
SECTIONS
{
. = 0x7C00;
.mbr : {
boot.o(.text);
boot.o(.*);
}
. = 0x7dfe;
.bootsig : {
SHORT(0xaa55);
}
. = 0x7e00;
.kernel : {
*(.text*);
*(.data*);
*(.rodata*);
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON);
*(.bss*);
}
. = ALIGN(4);
__bss_end = .;
/DISCARD/ : {
*(.eh_frame);
*(.comment);
}
}
To compile, link and generate a binary file that can be used in a disk image from this code, you could use commands like:
as --32 boot.S -o boot.o
gcc -c -m32 -ffreestanding -O3 kernel.c
gcc -ffreestanding -nostdlib -Wl,--build-id=none -m32 -Tlink.ld \
-o boot.elf -lgcc boot.o kernel.o
objcopy -O binary boot.elf boot.bin
The C standard says that static variables must be zero-initialized, even in absence of explicit initializer, so static char vgat_initialized= '\0'; is equivalent to static char vgat_initialized;.
In ELF and other similar formats, the zero-initialized data, such as this vgat_initialized goes to the .bss section. If you load such an executable yourself into memory, you need to explicitly zero the .bss part of the data segment.
The other answers are very complete and very helpful. In turns out that, in my specific case, I just needed to know that static variables initialized to 0 were put in .bss and not .data. Adding a .bss section to the linker script placed a zeroed-out section of memory in the image which solved the problem.
I would like to implement header files in my c-code which consists partly of GCC inline assembly code for 16 bit real mode but i seem to have linking problems. This is what my header file console.h looks like:
#ifndef CONSOLE_H
#define CONSOLE_H
extern void kprintf(char*);
#endif
and this is console.c:
#include "console.h"
void kprintf(char *string)
{
for(int i=0;string[i]!='\0';i++)
{
asm("mov $0x0e,%%ah;"
"mov $0x00,%%bh;"
"mov %0,%%al;"
"int $0x10"::"g"(string[i]):"eax", "ebx");
}
}
the last one hellworld.c:
asm("jmp main");
#include "console.h"
void main()
{
asm("mov $0x1000,%ax;"
"mov %ax,%es;"
"mov %ax,%ds");
char string[]="hello world";
kprintf(string);
asm(".rept 512;"
"hlt;"
".endr");
}
My bootloader is in bootloader.asm:
org 0x7c00
bits 16
section .text
mov ax,0x1000
mov ss,ax
mov sp,0x000
mov esp,0xfffe
xor ax,ax
mov es,ax
mov ds,ax
mov [bootdrive],dl
mov bh,0
mov bp,zeichen
mov ah,13h
mov bl,06h
mov al,1
mov cx,6
mov dh,010h
mov dl,01h
int 10h
load:
mov dl,[bootdrive]
xor ah,ah
int 13h
jc load
load2:
mov ax,0x1000
mov es,ax
xor bx,bx
mov ah,2
mov al,1
mov cx,2
xor dh,dh
mov dl,[bootdrive]
int 13h
jc load2
mov ax,0
mov es,ax
mov bh,0
mov bp,zeichen3
mov ah,13h
mov bl,06h
mov al,1
mov cx,13
mov dh,010h
mov dl,01h
int 10h
mov ax,0x1000
mov es,ax
mov ds,ax
jmp 0x1000:0x000
zeichen db 'hello2'
zeichen3 db 'soweit so gut'
bootdrive db 0
times 510 - ($-$$) hlt
dw 0xaa55
Now I use the following buildscript build.sh:
#!bin/sh
nasm -f bin bootloader.asm -o bootloader.bin
gcc hellworld.c -m16 -c -o hellworld.o -nostdlib -ffreestanding
gcc console.c -m16 -c -o console.o -nostdlib link.ld -ffreestanding
ld -melf_i386 -Ttext=0x0000 console.o hellworld.o -o hellworld.elf
objcopy -O binary hellworld.elf hellworld.bin
cat bootloader.bin hellworld.bin >disk.img
qemu-system-i386 disk.img
and the linkscript link.ld:
/*
* link.ld
*/
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
. = 0x0000;
.text : { *(.startup); *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
Unfortunately it isn't working because it doesn't print the expected hello world. I think there must be something wrong with the linking command:
ld -melf_i386 -Ttext=0x0000 console.o hellword.o link.ld -o hellworld.elf`
How do I link header-files in 16-bit mode correctly?
When I write the kprintf function directly in the hellworld.c it is working correctly. I am using Linux Mint Cinnamon Version 18 64 bit for development.
The header files are not really the issue at all. When you restructured the code and split it into multiple objects it has identified issues with how you build and how jmp main is placed into the final kernel file.
I have created a set of files that make all the adjustments discussed below if you wish to test the complete set of changes to see if they rectify your problems.
Although you show the linker script, you aren't actually using it. In your build file you have:
ld -melf_i386 -Ttext=0x0000 console.o hellworld.o -o hellworld.elf
It should be:
ld -melf_i386 -Tlink.ld console.o hellworld.o -o hellworld.elf
When using -c (compiles but doesn't link) with GCC don't specify link.ld as a linker script. The linker script can be specified at link time when you invoke LD. This line:
gcc console.c -m16 -c -o console.o -nostdlib link.ld -ffreestanding
Should be:
gcc console.c -m16 -c -o console.o -nostdlib -ffreestanding
In order for this linker script to locate the jmp main in a place that is first in the output kernel file you need to change:
asm("jmp main");
To:
asm(".pushsection .startup\r\n"
"jmp main\r\n"
".popsection\r\n");
The .pushsection temporarily changes the section to .startup, outputs the instruction jmp main and then restores the section with .popsection to whatever it was before. The linker script deliberately places anything in the .startup section before anything else. This ensures the jmp main (or any other instructions you place there) appear as the very first instructions of the output kernel file. The \r\n can be replaced by ; (semicolon). \r\n makes for prettier output if you ever have GCC generate an assembly file.
As mentioned in the comments of a now deleted question your kernel file exceeds the size of a single sector. When you don't have a linker script, the default one will place the data section after the code. Your code has repeated the hlt instruction so that your kernel is greater than 1 sector (512 bytes) and your bootloader only reads a single sector with Int 13h/AH=2h .
To rectify this remove:
asm(".rept 512;"
"hlt;"
".endr");
And replace it with:
asm("cli;"
"hlt;");
You should be mindful that as your kernel grows you'll need to adjust the number of sectors read in bootloader.asm to ensure all of the kernel is loaded into memory.
I also suggest that to keep QEMU, and other virtual machines happy that you simply generate a well known disk image size and place the bootloader and kernel inside it. Rather than:
cat bootloader.bin hellworld.bin >disk.img
Use this:
dd if=/dev/zero of=disk.img bs=1024 count=1440
dd if=bootloader.bin of=disk.img seek=0 conv=notrunc
dd if=hellworld.bin of=disk.img seek=1 conv=notrunc
The first command makes a zero filled file of 1440kb. This is the exact size of a 1.44MB floppy. The second command inserts bootloader.bin in the first sector without truncating the disk file. The third command places the kernel file into the disk images starting at the second sector on the disk without truncating the disk image.
I had made available a slightly improved linker script. It was amended to remove some of the potential cruft that the linker may insert into the kernel that won't be of much use and specifically identifies some of the sections like .rodata (read only data) etc.
/*
* link.ld
*/
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
. = 0x0000;
.text : { *(.startup); *(.text) }
.data : { *(.data); *(.rodata) }
.bss : { *(COMMON); *(.bss) }
/DISCARD/ : {
*(.eh_frame);
*(.comment);
*(.note.gnu.build-id);
}
}
Other Comments
Not related to your question but this code can be removed:
asm("mov $0x1000,%ax;"
"mov %ax,%es;"
"mov %ax,%ds");
You do this in bootloader.asm, so setting these segment registers again with the same value won't do anything useful.
You can improve the extended assembly template by using input constraints to pass the values you need via register EAX(AX) and EBX(BX) rather than coding the moves inside the template. Your code could have looked like:
void kprintf(const char *string)
{
while (*string)
{
asm("int $0x10"
:
:"a"((0x0e<<8) | *string++), /* AH = 0x0e, AL = char to print */
"b"(0)); /* BH = 0x00 page #
BL = 0x00 unused in text mode */
}
}
<< is the C bit shift left operator. 0x0e<<8 would shift 0x0e left 8 bits which would be 0x0e00. | is bitwise OR which effectively places the character to print in the lower 8 bits. That value is then passed into the EAX register by the assembly template via input constraint "a".
It is hard to say without knowing what your bootloader.asm does, but:
The link order must be wrong;
ld -melf_i386 -Ttext=0x0000 console.o hellworld.o -o hellworld.elf
should be:
ld -melf_i386 -Ttext=0x0000 hellworld.o console.o -o hellworld.elf
(Edit: I see that you have a linker script which would remove the need for this re-arrangement, but you're not using it for the link).
I suspect that your bootloader loads a single sector, and your padding:
asm(".rept 512;"
"hlt;"
".endr");
... prevents the code from the other object file from ever being loaded, since it pads hellword.o to (more than) the size of a sector.
The problem is nothing to do with the use of header files, it is because you have two compilation units which become separate objects, and the combined size of both when linked is larger than a sector (512 bytes).
UPDATE: Sure enough, it was a bug in the latest version of nasm. I "downgraded" and after fixing my code as shown in the answer I accepted, everything is working properly. Thanks, everyone!
I'm having problems with what should be a very simple program in 32-bit assembler on OS X.
First, the code:
section .data
hello db "Hello, world", 0x0a, 0x00
section .text
default rel
global _main
extern _printf, _exit
_main:
sub esp, 12 ; 16-byte align stack
push hello
call _printf
push 0
call _exit
It assembles and links, but when I run the executable it crashes with a segmentation fault: 11.
The command lines to assemble and link are:
nasm -f macho32 hello32x.asm -o hello32x.o
I know the -o there is not 100 percent necessary
Linking:
ld -lc -arch i386 hello32x.o -o hello32x
When I run it into lldb to debug it, everything is fine until it enters into the call to _printf, where it crashes as shown below:
(lldb) s
Process 1029 stopped
* thread #1: tid = 0x97a4, 0x00001fac hello32x`main + 8, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x00001fac hello32x`main + 8
hello32x`main:
-> 0x1fac <+8>: calll 0xffffffff991e381e
0x1fb1 <+13>: pushl $0x0
0x1fb3 <+15>: calll 0xffffffff991fec84
0x1fb8: addl %eax, (%eax)
(lldb) s
Process 1029 stopped
* thread #1: tid = 0x97a4, 0x991e381e libsystem_c.dylib`vfprintf + 49, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x991e381e libsystem_c.dylib`vfprintf + 49
libsystem_c.dylib`vfprintf:
-> 0x991e381e <+49>: xchgb %ah, -0x76f58008
0x991e3824 <+55>: popl %esp
0x991e3825 <+56>: andb $0x14, %al
0x991e3827 <+58>: movl 0xc(%ebp), %ecx
(lldb) s
Process 1029 stopped
* thread #1: tid = 0x97a4, 0x991e381e libsystem_c.dylib`vfprintf + 49, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x890a7ff8)
frame #0: 0x991e381e libsystem_c.dylib`vfprintf + 49
libsystem_c.dylib`vfprintf:
-> 0x991e381e <+49>: xchgb %ah, -0x76f58008
0x991e3824 <+55>: popl %esp
0x991e3825 <+56>: andb $0x14, %al
0x991e3827 <+58>: movl 0xc(%ebp), %ecx
As you can see toward the bottom, it stops due to a bad access error.
16-byte Stack Alignment
One serious issue with your code is stack alignment. 32-bit OS/X code requires 16-byte stack alignment at the point you make a CALL. The Apple IA-32 Calling Convention says this:
The function calling conventions used in the IA-32 environment are the same as those used in the System V IA-32 ABI, with the following exceptions:
Different rules for returning structures
The stack is 16-byte aligned at the point of function calls
Large data types (larger than 4 bytes) are kept at their natural alignment
Most floating-point operations are carried out using the SSE unit instead of the x87 FPU, except when operating on long double values. (The IA-32 environment defaults to 64-bit internal precision for the x87 FPU.)
You subtract 12 from ESP to align the stack to a 16 byte boundary (4 bytes for return address + 12 = 16). The problem is that when you make a CALL to a function the stack MUST be 16 bytes aligned just prior to the CALL itself. Unfortunately you push 4 bytes before the call to printf and exit. This misaligns the stack by 4, when it should be aligned to 16 bytes. You'll have to rework the code with proper alignment. As well you must clean up the stack after you make a call. If you use PUSH to put parameters on the stack you need to adjust ESP after your CALL to restore the stack to its previous state.
One naive way (not my recommendation) to fix the code would be to do this:
section .data
hello db "Hello, world", 0x0a, 0x00
section .text
default rel
global _main
extern _printf, _exit
_main:
sub esp, 8
push hello ; 4(return address)+ 8 + 4 = 16 bytes stack aligned
call _printf
add esp, 4 ; Remove arguments
push 0 ; 4 + 8 + 4 = 16 byte alignment again
call _exit ; This will not return so no need to remove parameters after
The code above works because we can take advantage of the fact that both functions (exit and printf) require exactly one DWORD being placed on the stack for parameters. 4 bytes for main's return address, 8 for the stack adjustment we made, 4 for the DWORD parameter = 16 byte alignment.
A better way to do this is to compute the amount of stack space you will need for all your stack based local variables (in this case 0) in your main function, plus the maximum number of bytes you will need for any parameters to function calls made by main and then make sure you pad enough bytes to make the value evenly divisible by 12. In our case the maximum number of bytes needed to be pushed for any one given function call is 4 bytes. We then add 8 to 4 (8+4=12) to become evenly divisible by 12. We then subtract 12 from ESP at the start of our function.
Instead of using PUSH to put parameters on the stack you can now move the parameters directly onto the stack into the space we have reserved. Because we don't PUSH the stack doesn't get misaligned. Since we didn't use PUSH we don't need to fix ESP after our function calls. The code could then look something like:
section .data
hello db "Hello, world", 0x0a, 0x00
section .text
default rel
global _main
extern _printf, _exit
_main:
sub esp, 12 ; 16-byte align stack + room for parameters passed
; to functions we call
mov [esp],dword hello ; First parameter at esp+0
call _printf
mov [esp], dword 0 ; First parameter at esp+0
call _exit
If you wanted to pass multiple parameters you place them manually on the stack as we did with a single parameter. If we wanted to print an integer 42 as part of our call to printf we could do it this way:
section .data
hello db "Hello, world %d", 0x0a, 0x00
section .text
default rel
global _main
extern _printf, _exit
_main:
sub esp, 12 ; 16-byte align stack + room for parameters passed
; to functions we call
mov [esp+4], dword 42 ; Second parameter at esp+4
mov [esp],dword hello ; First parameter at esp+0
call _printf
mov [esp], dword 0 ; First parameter at esp+0
call _exit
When run we should get:
Hello, world 42
16-byte Stack Alignment and a Stack Frame
If you are looking to create a function with a typical stack frame then the code in the previous section has to be adjusted. Upon entry to a function in a 32-bit application the stack is misaligned by 4 bytes because the return address was placed on the stack. A typical stack frame prologue looks like:
push ebp
mov ebp, esp
Pushing EBP into the stack after entry to your function still results in a misaligned stack, but it is misaligned now by 8 bytes (4 + 4).
Because of that the code must subtract 8 from ESP rather than 12. As well when determining the space needed to hold parameters, local stack variables, and pad bytes for alignment the stack allocation size will have to be evenly divisible by 8, not by 12. Code with a stack frame could look like:
section .data
hello db "Hello, world %d", 0x0a, 0x00
section .text
default rel
global _main
extern _printf, _exit
_main:
push ebp
mov ebp, esp ; Set up stack frame
sub esp, 8 ; 16-byte align stack + room for parameters passed
; to functions we call
mov [esp+4], dword 42 ; Second parameter at esp+4
mov [esp],dword hello ; First parameter at esp+0
call _printf
xor eax, eax ; Return value = 0
mov esp, ebp
pop ebp ; Remove stack frame
ret ; We linked with C library that calls _main
; after initialization. We can do a RET to
; return back to the C runtime code that will
; exit the program and return the value in EAX
; We can do this instead of calling _exit
Because you link with the C library on OS/X it will provide an entry point and do initialization before calling _main. You can call _exit but you can also do a RET instruction with the program's return value in EAX.
Yet Another Potential NASM Bug?
I discovered that NASM v2.12 installed via MacPorts on El Capitan seems to generate incorrect relocation entries for _printf and _exit, and when linked to a final executable the code doesn't work as expected. I observed almost the identical errors you did with your original code.
The first part of my answer still applies about stack alignment, however it appears you will need to work around the NASM issue as well. One way to do this install the NASM that comes with the latest XCode command line tools. This version is much older and only supports Macho-32, and doesn't support the default directive. Using my previous stack aligned code this should work:
section .data
hello db "Hello, world %d", 0x0a, 0x00
section .text
;default rel ; This directive isn't supported in older versions of NASM
global _main
extern _printf, _exit
_main:
sub esp, 12 ; 16-byte align stack
mov [esp+4], dword 42 ; Second parameter at esp+4
mov [esp],dword hello ; First parameter at esp+0
call _printf
mov [esp], dword 0 ; First parameter at esp+0
call _exit
To assemble with NASM and link with LD you could use:
/usr/bin/nasm -f macho hello32x.asm -o hello32x.o
ld -macosx_version_min 10.8 -no_pie -arch i386 -o hello32x hello32x.o -lc
Alternatively you could link with GCC:
/usr/bin/nasm -f macho hello32x.asm -o hello32x.o
gcc -m32 -Wl,-no_pie -o hello32x hello32x.o
/usr/bin/nasm is the location of the XCode command line tools version of NASM that Apple distributes. The version I have on El Capitan with latest XCode command line tools is:
NASM version 0.98.40 (Apple Computer, Inc. build 11) compiled on Jan 14 2016
I don't recommend NASM version 2.11.08 because it has a serious bug related to macho64 format. I recommend 2.11.09rc2. I have tested that version here and it does seem to work properly with the code above.
Yesterday i created my own u-boot module and want to set text base address at 0xd0020010.
But after compiling, in the .map file generated by linker shows like this
inker script and memory map
0x00000000 . = 0x0
0x00000000 . = ALIGN (0x4)
.text 0xd0020010 0x1f0
0xd0020010 __image_copy_start = .
*(.vectors)
*fill* 0xd0020010 0x10 00
.vectors 0xd0020020 0x60 arch/arm/lib/built-in.o
0xd0020020 _start
0xd0020044 _undefined_instruction
0xd0020048 _software_interrupt
0xd002004c _prefetch_abort
0xd0020050 _data_abort
0xd0020054 _not_used
0xd0020058 _irq
0xd002005c _fiq
You can see above the .vectors section, there are 16 bytes of 0x00 which name is "*fill*".
And my link script is like this
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
__image_copy_start = .;
*(.vectors)
CPUDIR/start.o (.text*)
*(.text*)
}
.........
I tried to remove ALIGH(4), but it stand still. And 0xd0020010 is a aligned address right? So it should have no matter to do with "ALIGH"
Although the 16 bytes of memory are filled by 0x00, which are nop instructions, but i still wonder why there is a "*fill*" section.
I'm trying to embed a pointer to a string in the code section using inline assembler. But gcc is adding a $ to the start of the symbol name, causing a link error.Here is a minimal example,
static const char str[] = "bar";
int main()
{
__asm__ __volatile__
(
"jmp 0f\n\t"
".long %0\n\t"
"0:"
:
: "i" ( str )
);
return 0;
}
building with
gcc -Wall -save-temps test.c -o test
gives the error
test.o: In function `main':
test.c:(.text+0x6): undefined reference to `$str'
looking at the .s temp file, can see the additional $ prepended to str
.file "test.c"
.section .rodata
.type str, #object
.size str, 4
str:
.string "bar"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
#APP
# 4 "test.c" 1
jmp 0f
.long $str
0:
# 0 "" 2
#NO_APP
movl $0, %eax
leave
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",#progbits
Think i am doing this the correct way, as the same approach works on ppc gcc,
<clip>
b 0f
.long str
0:
</clip>
Then again, maybe it is just "luck" it works for ppc. Is the issue because $ is used as a prefix for immediates when using the AT&T synax ?
In this simple example, i can work around the issue by hardcoding the symbol name, "str", in the inline assembler, but really need it to be an input constraint to the inline assembler.
Does anyone have any ideas on how to get this working on x86 targets ?
Thanks,
- Luke
The same thing happens using clang, probably because the code generator doesn't know the operand is bing used in a .long rather than as an immediate instruction operand. You code try something like:
const char str[] = "bar";
#define string(str) __asm__ __volatile__ \
( \
"jmp 0f\n\t" \
".long " #str "\n\t" \
"0:" \
)
int main()
{
string(str);
return 0;
}
(I had to remove the "static" on str because the compiler optimized it out as not being referenced.)