90 lines
2.9 KiB
Plaintext
90 lines
2.9 KiB
Plaintext
|
These illustrate an approach to initializing an address space suitable
|
||
|
for running a C program that uses newlib.
|
||
|
|
||
|
*** freestanding example apps:
|
||
|
|
||
|
stand00:
|
||
|
long-form %pcrel() assembler macros
|
||
|
|
||
|
stand01:
|
||
|
use pseudo-ops to implement crt0.S in an understandable manner
|
||
|
|
||
|
stand02:
|
||
|
- add some variables to the data and bss regions
|
||
|
- now there is somehting to zero-out
|
||
|
- note that the text region now is zero-padded in order to be able to initialize the page-aligned data region
|
||
|
- see the bss region get zeroed out this time
|
||
|
|
||
|
stand03:
|
||
|
call main --> main() { return 0x8675309 }
|
||
|
|
||
|
- the "call main" pseudo-operation is same as:
|
||
|
cm1: auipc x1,%pcrel_hi(main)
|
||
|
jalr x1,%pcrel_lo(callmain)(cm1)
|
||
|
|
||
|
- how does 'call' know to use x1 as the return address register??????
|
||
|
Because the ABI says so!
|
||
|
|
||
|
stand04:
|
||
|
call main() and set the value of a variable in the BSS
|
||
|
|
||
|
stand05:
|
||
|
call main() and set the values from variables in the DATA and BSS regions.
|
||
|
|
||
|
stand06:
|
||
|
Create a char array in main() that is not used. Even with an initializer it is optimized away!
|
||
|
|
||
|
stand07: (error)
|
||
|
Create char array in main() and then use it.
|
||
|
This examle fails due to missing memset()!!
|
||
|
|
||
|
stand08:
|
||
|
Add -lgcc and -lc libraries.
|
||
|
|
||
|
stand09: (error)
|
||
|
Add a call to malloc() that fails because there is no sbrk()
|
||
|
|
||
|
stand10: (note that the text region is too big to hardcode the data to start at 0x1000)
|
||
|
Add stubs for the newlib's stdlib dependancies in stub_stdlib.c
|
||
|
Demonstrate that malloc() is working properly.
|
||
|
|
||
|
|
||
|
Conclusion:
|
||
|
|
||
|
We can now execute single-threaded C programs that can use functions from the
|
||
|
standard library. There are no provisions, however, for things like I/O.
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
*** Related issues:
|
||
|
|
||
|
- use ABI names since x-this and x-that make thinking about arg-1 and arg-2 of a
|
||
|
C func calls easier. Show reg table from RVALP here.
|
||
|
|
||
|
- auipc + addi = how we put the address of some label into a register.
|
||
|
This method is optimized to eliminate accessing memory (where we could just store a pointer).
|
||
|
There is an 'la' pseudo-instruction:
|
||
|
la a1,target
|
||
|
... that tells the assembler to do this:
|
||
|
la1: auipc a1,%pcrel_hi(target)
|
||
|
addi a1,a1,%pcrel_lo(la1)
|
||
|
|
||
|
- pcrel_hi(label) represents an immediate-value suitable for auipc based on the label.
|
||
|
This immediate will be the distance from the PC to the label!!
|
||
|
- pcrel_lo(hi_label) is used to calc the right imm value for an addi insn matched to an auipc!
|
||
|
|
||
|
- lui + addi = how we put an absolute value into a register
|
||
|
There is an 'li' pseudo-instruction that can load any value into a register:
|
||
|
li a1,0x12345678
|
||
|
|
||
|
The li pseudo-operation on an RV32I will expand into this:
|
||
|
lui a1,%hi(0x12345678)
|
||
|
addi a1,a1,%lo(0x12345678)
|
||
|
|
||
|
On RV32 the assembler will only need to use either an addi or a lui and addi.
|
||
|
Note that 'li' works on RV64 and RV128 machines too. To accomplish the loading
|
||
|
the assembler will use a series of instructions like lui, addi, and shift.
|
||
|
|
||
|
|