560 Assignment
loader
A simple extension of the MTX booter is a loader, which loads an a.out file
into memory for execution. A loader is an essential (utility) function of an
operating system.
1. a.out file format:
Assume bs.s and bc.c are assembly and C source files. Under BCC,
ld86 -s bs.o bc.o /usr/lib/bcc/lic.a
generates an a.out file consisting of the following parts.
|header|Code|Data|...bss.....|
where bss is NOT in a.out but its size is recorded in the header.
The 32-byte header is composed of 8 longs.
0 0x04100301 //(combined I,D space) or 0x04200301=(separate I,D space)
1 0x00000020
2 text size // code size
3 data size // initialized data size
4 bss size // uninitialized data size ==> cleared to 0
5 0x00000000
6 total memory // ignore this field
7 symbolTable size // symbol Table size
If a.out has a combined I (Instruction) and D (Data) space, its code segment
and data+stack segment are all the same, i.e. CS,DS,and SS must be the same
segment during execution. This also means that the entire memory image must
be no greater than 64KB. ld86 -i generates a.out with separate I and D
spaces, in which case the code segment and data+stack segment will be
different, allowing a memory image of 128KB (64KB code segment and 64KB
data+stack segment).
2. The loader
To execute a.out, the loader first reads the file header to determine
totalMemorySize = tsize + dsize + bsize + (initial)stack_size
where the initial stack_size can be a default value, say 8KB, assuming
it can be expanded (if necessary) during execution. If the memory image of
a loaded a.out cannot be changed, then a max. default stack_size is usually
used. Then the loader allocates the needed memory space by
ushort segment = OSmalloc(totalMemorySize);
where OSmalloc() is the memroy manager of the OS.
Then, the loader loads the |code|data| of a.out into memory (starting at
segment) and clears the bss section to 0 (so that all the uninitialized
globals start with 0 value). Finally, the OS sends CPU to segment to execute
a.out.
3. Specification:
Implement a loader that loads an a.out file to memory and execute it.
4. HELP Files:
4-1. YOUR MTX booter as in A1. It boots /boot/mtx to the segment 0x1000.
4-2. Instead of the real MTX, we shall use a S(Simple)OS generated by the
following ts.s and tc.c files.
-------------------------------------------------
bcc -c tc.c
as86 -o ts.o ts.s
ld86 -d ts.o tc.o /usr/lib/bcc/lic.a
mount /dev/fd0 /fd0
cp a.out /fd0/boot/sos (you may name it mtx)
umount /fd0
-------------------------------------------------
(1). ts.s file of SOS:
.globl begtext, begdata, begbss ! needed by linker
.globl _getc,_putc,_diskr,_setes,_inces,_error ! EXPORT
.globl _get_byte,_put_byte, _go ! EXPORT
.globl _main,_prints ! IMPORT these
.text ! these tell as:
begtext: ! text,data,bss segments
.data ! are all the same.
begdata:
.bss
begbss:
.text
start:
mov ax,cs ! set segment registers for CPU
mov ds,ax ! we know CS=0x1000. Let DS=ES=CS
mov es,ax ! ES = CS
mov ss,ax ! SS = CS ===> all point at 0x1000
mov sp,#0 ! SP = 64KB, at 0x2000
call _main ! call main() in C
dead: jmp dead
!---------------------------------------
! diskr(cyl, head, sector, buf) all count from 0
! 4 6 8 10
!---------------------------------------
_diskr:
push bp
mov bp,sp ! bp = stack frame pointer
movb dl, #0x00 ! drive 0=FD0
movb dh, 6[bp] ! head
movb cl, 8[bp] ! sector
incb cl ! inc sector by 1 to suit BIOS
movb ch, 4[bp] ! cyl
mov bx, 10[bp] ! BX=buf ==> memory addr=(ES,BX)
mov ax, #0x0202 ! READ 2 sectors to (EX, BX)
int 0x13 ! call BIOS to read the block
jb _error ! to error if CarryBit is on [read failed]
pop bp
ret
!------------------------------
! error & reboot
!------------------------------
_error:
mov bx, #bad
push bx
call _prints
int 0x19 ! reboot
bad: .asciz "Error!\n\r"
!======================== I/O functions =================================
!---------------------------------------------
! char getc() function: returns a char
!---------------------------------------------
_getc:
xorb ah,ah ! clear ah
int 0x16 ! call BIOS to get a char in AX
ret
!----------------------------------------------
! void putc(char c) function: print a char
!----------------------------------------------
_putc:
push bp
mov bp,sp
movb al,4[bp] ! get the char into aL
movb ah,#14 ! aH = 14
movb bl,#0x0B ! bL = CYAN color
int 0x10 ! call BIOS to display the char
pop bp
ret
_setes: push bp
mov bp,sp
mov ax,4[bp]
mov es,ax
pop bp
ret
_inces: ! inces() inc ES by 0x40, or 1K
mov ax,es
add ax,#0x40
mov es,ax
ret
_go: ! go(segment)
push bp
mov sp,bp
mov ax,4[bp]
mov segment,ax
! jmpi 0, segment
opcode: .byte 0xEA ! jmpi
.word 0 ! offset
segment: .word 0 ! segment
!*===========================================================================*
!* get_byte *
!*===========================================================================*
! c = get_byte(segment, offset)
_get_byte:
push bp ! save bp
mov bp,sp ! stack frame pointer
push es ! save es
mov es,4[bp] ! load es with segment value
mov bx,6[bp] ! load bx with offset from segment
seg es ! go get the byte
movb al,[bx] ! al = byte
xorb ah,ah ! ax = byte
pop es ! restore es
pop bp ! restore bp
ret ! return to caller
!*===========================================================================*
!* put_byte *
!*===========================================================================*
! put_byte(char,segment,offset)
_put_byte:
push bp ! save bp
mov bp,sp ! stack frame pointer
push es ! save es
push bx
mov es,6[bp] ! load es with seg value
mov bx,8[bp] ! load bx with offset from segment
movb al,4[bp] ! load byte in aL
seg es ! go put the byte to [ES, BX]
movb [bx],al ! there it goes
pop bx ! restore bx
pop es ! restore es
pop bp ! restore bp
ret ! return to caller
(2). tc.c file of SOS:
#include "ext2.h" // EXT2 INODE, DIR struct types
#include "io.c" // YOUR printf() with "%c %s %d %x %l\n";
#include "loader.c" // YOUR loader.c file
char filename[64];
ushort segment;
main()
{
printf("\nSimple OS running\n");
while(1){
printf("enter filename to execute : ");
gets(filename);
segment = 0x2000; // choose a free segment
if (load(filename, segment)){
printf("jmp to segment %s\n", segment\n");
go(segment);
}
else{
printf("exec ERROR : file not found\n");
}
}
}
(3). The executable file /bin/u1 is generated by
--------------------------------------------------
bcc -c uc.c
as86 -o us.o us.s
ld86 -s us.o uc.o /usr/lib/bcc/libc.a
mount /dev/fd0 /fd0
cp a.out /fd0/bin/u1
umount /fd0
--------------------------------------------------
3-1. us.s file
.globl begtext, begdata, begbss ! needed by linker
.globl _getc,_putc ! EXPORT
.globl _main,_prints ! IMPORT these
.text ! these tell as:
begtext: ! text,data,bss segments
.data ! are all the same.
begdata:
.bss
begbss:
.text
start:
mov ax,cs ! set segment registers for CPU
mov ds,ax ! we know CS=segment. Let DS=SS
mov es,ax ! ES = CS
mov ss,ax ! SS = CS ===> all point at segment
mov sp,#0 ! SP = 64KB
call _main ! call main() in C
gomtx: jmpi 0, 0x1000 ! jmp back to SOS at 0x1000
!---------------------------------------------
! char getc() function: returns a char
!---------------------------------------------
_getc:
xorb ah,ah ! clear ah
int 0x16 ! call BIOS to get a char in AX
ret
!----------------------------------------------
! void putc(char c) function: print a char
!----------------------------------------------
_putc:
push bp
mov bp,sp
movb al,4[bp] ! get the char into aL
movb ah,#14 ! aH = 14
movb bl,#0x0C ! bL = RED color
int 0x10 ! call BIOS to display the char
pop bp
ret
3-2 uc.c file
#include "io.c" // YOUR gets(), printf(), etc.
main()
{
char name[64];
ushort segment;
printf("\nThis is u1 running\n");
while(1){
printf("what's your name? : ");
gets(name);
if (strcmp(name, "quit")==0){
printf("quit : go back to OS at 0x1000\n");
return;
}
printf("Welcome %s\n", name);
}
}
===========================================================================
In ~samples/LAB2/ sosimage.gz is a diskimage of a bootable sos system.
Download and dump it to a floppy disk. Boot up SOS and try to run it.
You only need to replace the booter with YOUR booter from Lab#1
AND generate YOUR SOS with YOUR loader.c and io.c files.
===========================================================================
Programming Tasks:
1. Develop your own printf(fmt) function, which can print with
fmt = "....%c...%s ...%d ... %x ...%l ...\n"
2. Use get_byte()/put_byte() to implement YOUR own
ushort get_word(segment, offset) ushort segment, offset;
int put_word(word, segment, offset) ushort word,segment,offset;
functions in C for read/write a word from/to memory (segment,offset).
3. Implement YOUR loader.c
load(pathname, segment) char *pathname; ushort segment;
{
1. break up pathname into tokens:
/a/b/c/d ==> "a", "b", "c", "d" with n=4
2. locate the inode of /a/b/c/d: let ip->INODE of file
return 0 if can't find the file
3. read file header to get size information;
print tsize, dsize, bsize
3. load a.out data blocks (with header) into segment.
move the executable part (|code|dat|....) upward 32 bytes to
eliminate the header.
4. Clear bss section (after |code|data|) to 0.
5. return 1 for success.