├── got_plt ├── mylib.c ├── main.c ├── startup.S ├── stm32_linker.ld ├── Makefile └── README.md ├── semihosting ├── mylib.c ├── main.c ├── README.md ├── startup.S ├── stm32_linker.ld └── Makefile ├── part1-deadbeef ├── stm32_sections_only.ld ├── stm32_base.ld ├── main.S └── README.md ├── part2-makefile ├── stm32_sections_only.ld ├── stm32_base.ld ├── README.md ├── main.S └── Makefile ├── part4-function_call ├── README.md ├── main.c ├── startup.S ├── Makefile └── stm32_linker.ld ├── part3-get_to_main ├── README.md ├── main.c ├── startup.S ├── Makefile └── stm32_linker.ld └── README.md /got_plt/mylib.c: -------------------------------------------------------------------------------- 1 | unsigned int library_function( unsigned int x ) 2 | { 3 | return(x+3); 4 | } 5 | -------------------------------------------------------------------------------- /semihosting/mylib.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | unsigned int library_function( unsigned int x ) 4 | { 5 | return(x+3); 6 | } 7 | -------------------------------------------------------------------------------- /part1-deadbeef/stm32_sections_only.ld: -------------------------------------------------------------------------------- 1 | _estack = 0x20001000; 2 | SECTIONS 3 | { 4 | . = 0x08000000; /* The starting address of flash */ 5 | } 6 | -------------------------------------------------------------------------------- /part2-makefile/stm32_sections_only.ld: -------------------------------------------------------------------------------- 1 | _estack = 0x20001000; 2 | SECTIONS 3 | { 4 | . = 0x08000000; /* The starting address of flash */ 5 | } 6 | -------------------------------------------------------------------------------- /part1-deadbeef/stm32_base.ld: -------------------------------------------------------------------------------- 1 | /* Define the end of RAM and limit of stack memory */ 2 | /* (64KB SRAM on the STM32F429 line, 4096 = 0x1000) */ 3 | /* (RAM starts at address 0x20000000) */ 4 | _estack = 0x2000FA00; 5 | 6 | MEMORY 7 | { 8 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K 9 | SRAM (rxw) : ORIGIN = 0x20000000, LENGTH = 64K 10 | 11 | } 12 | -------------------------------------------------------------------------------- /part2-makefile/stm32_base.ld: -------------------------------------------------------------------------------- 1 | /* Define the end of RAM and limit of stack memory */ 2 | /* (64KB SRAM on the STM32F429 line, 4096 = 0x1000) */ 3 | /* (RAM starts at address 0x20000000) */ 4 | _estack = 0x2000FA00; 5 | 6 | MEMORY 7 | { 8 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K 9 | SRAM (rxw) : ORIGIN = 0x20000000, LENGTH = 64K 10 | 11 | } 12 | -------------------------------------------------------------------------------- /got_plt/main.c: -------------------------------------------------------------------------------- 1 | 2 | // This function is defined in mylib.c 3 | extern unsigned int library_function( unsigned int x ); 4 | 5 | // These global variables will be in the bss section 6 | unsigned int x; 7 | unsigned int y; 8 | unsigned int z; 9 | 10 | int main ( void ) 11 | { 12 | x=7; 13 | y=8; 14 | z=library_function(77); 15 | 16 | while(1){ 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /semihosting/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void initialise_monitor_handles(void); 4 | extern unsigned int library_function( unsigned int x ); 5 | 6 | unsigned int x = 5; 7 | 8 | int main(void) { 9 | #if ENABLE_SEMIHOSTING 10 | initialise_monitor_handles(); 11 | 12 | // don't buffer on stdout 13 | setbuf(stdout, NULL); 14 | //~ setvbuf(stdout, NULL, _IONBF, 0); 15 | printf("Hello World!\n"); 16 | #endif 17 | 18 | #if ENABLE_PIC 19 | x = library_function(10); 20 | #endif 21 | printf("Value of x is %d\n",x); 22 | 23 | while (1) { 24 | 25 | }; 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /part4-function_call/README.md: -------------------------------------------------------------------------------- 1 | ## Part 4 2 | ### How is a function called 3 | 4 | The ARM Procedure Call standard states that for a function call 5 | The arguments are stored in r0,r1,r2,r3 and if there are more than that 6 | then it is stored on the stack. The return value of the function is stored 7 | in r0. 8 | 9 | #### Commands to build: 10 | - Make objcode ``make`` 11 | - Check disassembly using ``arm-none-eabi-objdump -d --source main.o`` 12 | 13 | ### References 14 | 1. https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/function-parameters-on-32-bit-arm 15 | 2. https://developer.arm.com/docs/ihi0042/latest 16 | -------------------------------------------------------------------------------- /part2-makefile/README.md: -------------------------------------------------------------------------------- 1 | ## Part 2 2 | ### Creating a Makefile 3 | 4 | This example demonstrates creation of a makefile to generate the elf and bin files. 5 | Makefile will basically run the commands we used to build the previous project automatically 6 | 7 | ### Commands to build: 8 | ``make all`` 9 | 10 | ### References 11 | 1. https://makefiletutorial.com/ 12 | - Excellent step by step tutorial for make 13 | 2. http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ 14 | - A more simpler tutorial 15 | 3. https://www.gnu.org/software/make/manual/make.html 16 | - Complete make documentation 17 | 4. https://stackoverflow.com/questions/16931770/makefile4-missing-separator-stop 18 | - A solution to a common error 19 | 20 | -------------------------------------------------------------------------------- /part3-get_to_main/README.md: -------------------------------------------------------------------------------- 1 | ## Part 3 2 | ### Getting to main function in C 3 | 4 | This example builds up on the previous code and shows how 5 | the main function usually defined in the C file is reached 6 | 7 | See the comments in the linker script as there are a few changes in it 8 | 9 | #### Commands to build: 10 | ``make`` 11 | 12 | ### References 13 | 1. https://vivonomicon.com/2018/04/20/bare-metal-stm32-programming-part-2-making-it-to-main/ 14 | 2. https://stackoverflow.com/questions/381244/purpose-of-memory-alignment 15 | 3. https://developer.ibm.com/articles/pa-dalign/ 16 | - Memory alignment and its importance 17 | 4. https://interrupt.memfault.com/blog/code-size-optimization-gcc-flags#linker-garbage-collection 18 | - Link time garbage collection and the KEEP command in linker script 19 | 5. https://community.st.com/s/question/0D50X00009XkZDa/understanding-alignment-in-stm32f405xg-ld-linker-script 20 | - Memory alignment clarification 21 | -------------------------------------------------------------------------------- /part4-function_call/main.c: -------------------------------------------------------------------------------- 1 | 2 | extern unsigned int _sidata,_sdata,_edata; // These values are defined in the linker script 3 | extern unsigned int _sbss,_ebss; // based on the values of the memory regions 4 | 5 | 6 | int test_function2(int a, int b, int c, int d, int e); 7 | 8 | int main(void){ 9 | test_function2(1,2,3,4,5); 10 | while(1){ 11 | 12 | } 13 | } 14 | 15 | int test_function2(int a, int b, int c, int d, int e){ 16 | return a+b+c+d+e; 17 | } 18 | 19 | void copy_src(){ 20 | /* The reset_handler will be the first function to be called. 21 | * In the reset_handler we will copy the .data section from 22 | * FLASH to RAM and initialize the BSS section with 0s */ 23 | 24 | unsigned int *src, *dst; 25 | 26 | // Copy from _sidata (FLASH) to _sdata (RAM) 27 | for(src = &_sidata, dst = &_sdata; dst < &_edata; src++, dst++){ 28 | *dst = *src; 29 | } 30 | 31 | // Zero out the .bss section 32 | src = &_sbss; 33 | while(src < &_ebss){ 34 | *src = 0; 35 | src++; 36 | } 37 | 38 | // Call main 39 | main(); 40 | } 41 | -------------------------------------------------------------------------------- /part1-deadbeef/main.S: -------------------------------------------------------------------------------- 1 | // These instructions define attributes of our chip and 2 | // the assembly language we'll use: 3 | .syntax unified 4 | .cpu cortex-m4 5 | .fpu softvfp 6 | .thumb 7 | 8 | // Global memory locations. 9 | .global vtable 10 | .global reset_handler 11 | 12 | /* 13 | * The actual vector table. 14 | * Only the size of RAM and 'reset' handler are 15 | * included, for simplicity. 16 | */ 17 | .type vtable, %object 18 | vtable: 19 | .word _estack 20 | .word reset_handler 21 | .size vtable, .-vtable 22 | 23 | 24 | 25 | /* 26 | * The Reset handler. Called on reset. 27 | */ 28 | .type reset_handler, %function 29 | reset_handler: 30 | // Set the stack pointer to the end of the stack. 31 | // The '_estack' value is defined in our linker script. 32 | LDR r0, =_estack 33 | MOV sp, r0 34 | 35 | // Set some dummy values. When we see these values 36 | // in our debugger, we'll know that our program 37 | // is loaded on the chip and working. 38 | LDR r7, =0xDEADBEEF 39 | MOVS r0, #0 40 | main_loop: 41 | // Add 1 to register 'r0'. 42 | ADDS r0, r0, #1 43 | // Loop back. 44 | B main_loop 45 | .size reset_handler, .-reset_handler 46 | -------------------------------------------------------------------------------- /part2-makefile/main.S: -------------------------------------------------------------------------------- 1 | // These instructions define attributes of our chip and 2 | // the assembly language we'll use: 3 | .syntax unified 4 | .cpu cortex-m4 5 | .fpu softvfp 6 | .thumb 7 | 8 | // Global memory locations. 9 | .global vtable 10 | .global reset_handler 11 | 12 | /* 13 | * The actual vector table. 14 | * Only the size of RAM and 'reset' handler are 15 | * included, for simplicity. 16 | */ 17 | .type vtable, %object 18 | vtable: 19 | .word _estack 20 | .word reset_handler 21 | .size vtable, .-vtable 22 | 23 | 24 | 25 | /* 26 | * The Reset handler. Called on reset. 27 | */ 28 | .type reset_handler, %function 29 | reset_handler: 30 | // Set the stack pointer to the end of the stack. 31 | // The '_estack' value is defined in our linker script. 32 | LDR r0, =_estack 33 | MOV sp, r0 34 | 35 | // Set some dummy values. When we see these values 36 | // in our debugger, we'll know that our program 37 | // is loaded on the chip and working. 38 | LDR r7, =0xDEADBEEF 39 | MOVS r0, #0 40 | main_loop: 41 | // Add 1 to register 'r0'. 42 | ADDS r0, r0, #1 43 | // Loop back. 44 | B main_loop 45 | .size reset_handler, .-reset_handler 46 | -------------------------------------------------------------------------------- /part3-get_to_main/main.c: -------------------------------------------------------------------------------- 1 | 2 | extern unsigned int _sidata,_sdata,_edata; // These values are defined in the linker script 3 | extern unsigned int _sbss,_ebss; // based on the values of the memory regions 4 | 5 | char global_data = 27; // Check in gdb if it is 27 6 | char global_bss; // Check in gdb if it is 0 7 | 8 | /* Run the main loop infinitely */ 9 | int main(){ 10 | char main_data = 0x27; // Check in gdb if it is 0x28 11 | main_data+=1; // because we are adding 1 to it 12 | while(1){ 13 | 14 | } 15 | 16 | return 0; 17 | } 18 | 19 | void copy_src(){ 20 | /* The reset_handler will be the first function to be called. 21 | * In the reset_handler we will copy the .data section from 22 | * FLASH to RAM and initialize the BSS section with 0s */ 23 | 24 | unsigned int *src, *dst; 25 | 26 | // Copy from _sidata (FLASH) to _sdata (RAM) 27 | for(src = &_sidata, dst = &_sdata; dst < &_edata; src++, dst++){ 28 | *dst = *src; 29 | } 30 | 31 | // Zero out the .bss section 32 | src = &_sbss; 33 | while(src < &_ebss){ 34 | *src = 0; 35 | src++; 36 | } 37 | 38 | // Call main 39 | main(); 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### STM32 Projects 2 | 3 | Projects to begin learning about low-level embedded programming on the STM32 platform 4 | The examples are created for the STM32F429-DISC1 Discovery board 5 | but should be portable to any other STM32 mcu 6 | 7 | 8 | #### References 9 | - General Embedded systems 10 | - https://www.coursera.org/learn/introduction-embedded-systems 11 | - Excellent cource about embedded systems. Not using high level IDE and stuff. In parallel check out the section on understanding ELF. 12 | - https://www.coursera.org/learn/embedded-software-hardware 13 | 14 | - Understanding ELF files and some basic tools to analyze them 15 | - https://www.youtube.com/watch?v=xkD2NyRpvI4 16 | - Requires some microcontroller knowledge 17 | - Important point to remember is that all peripherals in a processor have an address associated with it, even the GPIO has an address. 18 | - https://www.youtube.com/watch?v=t09LFtfy4JU&list=PLUFkSN0XLZ-n_Na6jwqopTt1Ki57vMIc3&index=36 19 | - A couple of lectures of this course are related to elf From Day2P18 to Day2P28 20 | - Teaches about readelf and hexdump, objdump -d (disassembly) 21 | - https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis/ 22 | 23 | - STM32 specific 24 | - https://leanpub.com/mastering-stm32 25 | -------------------------------------------------------------------------------- /semihosting/README.md: -------------------------------------------------------------------------------- 1 | ## Part 6 2 | ### ARM Semihosting 3 | 4 | Semihosting is a way for the microcontroller to use the hosts (PCs) I/O capabilities. With this we can use functions like printf 5 | to send strings to the host. 6 | 7 | #### Commands to build: 8 | - ``make no_semihosting`` will make code without the semihosting functionality 9 | - ``make simple_semihosting`` will make code with semihosting functionality and print "Hello World" to the gdb server. See below 10 | how to enable semihosting support on the host. 11 | - ``make pic_semihosting`` will make pic with semihosting support. (Not tested yet.) 12 | 13 | #### How to setup semihosting using gdb 14 | - This functionality should be supported by the gdb server. 15 | - openocd and st-util supports this functionality 16 | - openocd: 17 | - Use command ``monitor arm semihosting enable`` in the gdb client it will show prints on the *openocd console*. 18 | - st-util 19 | - Use command ``monitor semihosting enable`` in the gdb client it will show prints in a file call *:tt* on the folder where you run st-util. 20 | 21 | ### References 22 | 1. http://www.keil.com/support/man/docs/armcc/armcc_pge1358787046598.htm 23 | 2. https://developer.arm.com/docs/dui0471/k/what-is-semihosting/what-is-semihosting 24 | 3. https://shawnhymel.com/1840/how-to-use-semihosting-with-stm32/ 25 | -------------------------------------------------------------------------------- /part3-get_to_main/startup.S: -------------------------------------------------------------------------------- 1 | // These instructions define attributes of our chip and 2 | // the assembly language we'll use: 3 | .syntax unified 4 | .cpu cortex-m4 5 | .fpu softvfp 6 | .thumb 7 | 8 | // Global memory locations. 9 | .global vtable 10 | .global reset_handler 11 | 12 | /* 13 | * The actual vector table. 14 | * Only the size of RAM and 'reset' handler are 15 | * included, for simplicity. 16 | */ 17 | .type vtable, %object 18 | /* This is additional line that is required to be added so that 19 | * we create a new section header called vector_table in the object file 20 | * which will be placed in the vector_table section of the output elf by 21 | * the linker */ 22 | .section .vector_table,"a",%progbits 23 | vtable: 24 | .word _estack 25 | .word reset_handler 26 | .size vtable, .-vtable 27 | 28 | 29 | 30 | /* 31 | * The Reset handler. Called on reset. 32 | */ 33 | .type reset_handler, %function 34 | reset_handler: 35 | // Set the stack pointer to the end of the stack. 36 | // The '_estack' value is defined in our linker script. 37 | LDR r0, =_estack 38 | MOV sp, r0 39 | 40 | // Set some dummy values. When we see these values 41 | // in our debugger, we'll know that our program 42 | // is loaded on the chip and working. 43 | // Set a breakpoint at copy_src to see DEADBEEF 44 | LDR r7, =0xDEADBEEF 45 | MOVS r0, #0 46 | B copy_src 47 | .size reset_handler, .-reset_handler 48 | -------------------------------------------------------------------------------- /part4-function_call/startup.S: -------------------------------------------------------------------------------- 1 | // These instructions define attributes of our chip and 2 | // the assembly language we'll use: 3 | .syntax unified 4 | .cpu cortex-m4 5 | .fpu softvfp 6 | .thumb 7 | 8 | // Global memory locations. 9 | .global vtable 10 | .global reset_handler 11 | 12 | /* 13 | * The actual vector table. 14 | * Only the size of RAM and 'reset' handler are 15 | * included, for simplicity. 16 | */ 17 | .type vtable, %object 18 | /* This is additional line that is required to be added so that 19 | * we create a new section header called vector_table in the object file 20 | * which will be placed in the vector_table section of the output elf by 21 | * the linker */ 22 | .section .vector_table,"a",%progbits 23 | vtable: 24 | .word _estack 25 | .word reset_handler 26 | .size vtable, .-vtable 27 | 28 | 29 | 30 | /* 31 | * The Reset handler. Called on reset. 32 | */ 33 | .type reset_handler, %function 34 | reset_handler: 35 | // Set the stack pointer to the end of the stack. 36 | // The '_estack' value is defined in our linker script. 37 | LDR r0, =_estack 38 | MOV sp, r0 39 | 40 | // Set some dummy values. When we see these values 41 | // in our debugger, we'll know that our program 42 | // is loaded on the chip and working. 43 | // Set a breakpoint at copy_src to see DEADBEEF 44 | LDR r7, =0xDEADBEEF 45 | MOVS r0, #0 46 | B copy_src 47 | .size reset_handler, .-reset_handler 48 | -------------------------------------------------------------------------------- /part1-deadbeef/README.md: -------------------------------------------------------------------------------- 1 | ## Part 1 2 | ### Bare metal assembler and linker 3 | 4 | This example demonstrates the simple assembler code and linker usage by using a few registers 5 | 6 | Commands to build: 7 | #### Compile/Assemble 8 | - arm-none-eabi-gcc -x assembler-with-cpp -c -O0 -mcpu=cortex-m4 -mthumb -Wall main.S -o main.o 9 | 10 | Assemble the assembly file into object code witht the AMR thumb instruction set 11 | -x assembler-with-cpp => Use C preprocessor 12 | -c => Only compile, do not link 13 | -O0 => Optimization level to 0 14 | -mcpu=cortex-m4 => Use the cortex-m4 cpu 15 | -mthumb => Use thumb instruction set 16 | -Wall => Show more warnings 17 | main.S => Input assembly file 18 | -o main.o => Output object file 19 | 20 | #### Link 21 | - arm-none-eabi-gcc main.o -mcpu=cortex-m4 -mthumb -Wall -nostdlib -lgcc -T./stm32_sections_only.ld -o main.elf 22 | 23 | main.o => Input object file 24 | -nostdlib => do not link with the C std lib 25 | -lgcc => Use the gcc lib 26 | -T./stm32_base.ld => Linker script to be used 27 | -o main.elf => Output elf that can be loaded to mcu 28 | 29 | #### Load 30 | Install stlink utility from https://github.com/texane/stlink/blob/master/doc/tutorial.md 31 | [Optional] Copy binaries to /usr/local/bin and libs to /usr/local/lib 32 | [If above step is performed] ``ldconfig`` 33 | Start gdb server using ``st-util`` 34 | Start gdb using ``arm-none-eabi-gdb `` 35 | Connect to gdb server ``target extended localhost:4242`` 36 | Load the elf ``load `` 37 | Run program ``r`` 38 | Stop after sometime ``ctrl+c`` and examine the registers ``info registers`` you should see deadbeef at r7 39 | 40 | 41 | 42 | ### References 43 | 1. https://vivonomicon.com/2018/04/02/bare-metal-stm32-programming-part-1-hello-arm/ 44 | 2. https://www.st.com/content/ccc/resource/technical/document/programming_manual/6c/3a/cb/e7/e4/ea/44/9b/DM00046982.pdf/files/DM00046982.pdf/jcr:content/translations/en.DM00046982.pdf 45 | - See section about Vector table 46 | 3. https://www.mikrocontroller.net/articles/ARM-ASM-Tutorial#Writing_assembly_applications 47 | - See section titled 'Defining symbols in linker scripts' 48 | 4. https://sourceware.org/binutils/docs/ld/Scripts.html 49 | - Complete information about linker scripts, see important links below 50 | - https://sourceware.org/binutils/docs/ld/MEMORY.html#MEMORY 51 | - https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS 52 | - https://sourceware.org/binutils/docs/ld/Simple-Example.html#Simple-Example 53 | -------------------------------------------------------------------------------- /part2-makefile/Makefile: -------------------------------------------------------------------------------- 1 | 2 | #################### CONFIGURABLE SECTION ########################### 3 | # Target can be anything you want. It will create an elf and bin file 4 | # with this name 5 | TARGET = main 6 | 7 | # Define the linker script location and chip architecture. 8 | # Change this based on your linker script name and chip architecture 9 | LD_SCRIPT = stm32_sections_only.ld 10 | MCU_SPEC = cortex-m4 11 | 12 | # Add assembler and C files to this, later we can add a wildcard 13 | # to compile all c files in the directory 14 | AS_SRC = ./main.S 15 | C_SRC = 16 | 17 | # Toolchain definitions (ARM bare metal defaults) 18 | # Set the TOOLCHAIN_PATH variable to the path where it is installed 19 | # If it is accessible globally. ie it is in your system path ($PATH) 20 | # then leave it blank. A slash at the end of the path is required 21 | # eg: TOOLCHAIN_PATH = /usr/local/bin/ 22 | TOOLCHAIN_PATH = 23 | TOOLCHAIN = $(TOOLCHAIN_PATH)arm-none-eabi- 24 | CC = $(TOOLCHAIN)gcc 25 | AS = $(TOOLCHAIN)as 26 | LD = $(TOOLCHAIN)ld 27 | OC = $(TOOLCHAIN)objcopy 28 | OD = $(TOOLCHAIN)objdump 29 | OS = $(TOOLCHAIN)size 30 | 31 | ###################################################################### 32 | 33 | # Assembly directives. 34 | ASFLAGS += -c 35 | ASFLAGS += -O0 36 | ASFLAGS += -mcpu=$(MCU_SPEC) 37 | ASFLAGS += -mthumb 38 | ASFLAGS += -Wall 39 | # (Set error messages to appear on a single line.) 40 | ASFLAGS += -fmessage-length=0 41 | 42 | # C compilation directives 43 | CFLAGS += -mcpu=$(MCU_SPEC) 44 | CFLAGS += -mthumb 45 | CFLAGS += -Wall 46 | CFLAGS += -g 47 | # (Set error messages to appear on a single line.) 48 | CFLAGS += -fmessage-length=0 49 | # (Set system to ignore semihosted junk) 50 | CFLAGS += --specs=nosys.specs 51 | 52 | # Linker directives 53 | LSCRIPT = ./$(LD_SCRIPT) 54 | LFLAGS += -mcpu=$(MCU_SPEC) 55 | LFLAGS += -mthumb 56 | LFLAGS += -Wall 57 | LFLAGS += --specs=nosys.specs 58 | LFLAGS += -nostdlib 59 | LFLAGS += -lgcc 60 | LFLAGS += -T$(LSCRIPT) 61 | 62 | OBJS = $(AS_SRC:.S=.o) 63 | OBJS += $(C_SRC:.c=.o) 64 | 65 | # The PHONY keyword is required so that makefile does not 66 | # consider the rule 'all' as a file 67 | .PHONY: all 68 | all: $(TARGET).bin 69 | 70 | # There should be a tab here on line 71, 4 spaces does not work 71 | %.o: %.S 72 | $(CC) -x assembler-with-cpp $(ASFLAGS) $< -o $@ 73 | 74 | %.o: %.c 75 | $(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@ 76 | 77 | $(TARGET).elf: $(OBJS) 78 | $(CC) $^ $(LFLAGS) -o $@ 79 | 80 | $(TARGET).bin: $(TARGET).elf 81 | $(OC) -S -O binary $< $@ 82 | $(OS) $< 83 | 84 | .PHONY: clean 85 | clean: 86 | rm -f $(OBJS) 87 | rm -f $(TARGET).elf $(TARGET).bin 88 | 89 | -------------------------------------------------------------------------------- /part3-get_to_main/Makefile: -------------------------------------------------------------------------------- 1 | 2 | #################### CONFIGURABLE SECTION ########################### 3 | # Target can be anything you want. It will create an elf and bin file 4 | # with this name 5 | TARGET = main 6 | 7 | # Define the linker script location and chip architecture. 8 | # Change this based on your linker script name and chip architecture 9 | LD_SCRIPT = stm32_linker.ld 10 | MCU_SPEC = cortex-m4 11 | 12 | # Add assembler and C files to this, later we can add a wildcard 13 | # to compile all c files in the directory 14 | AS_SRC = ./startup.S 15 | C_SRC = ./main.c 16 | 17 | # Toolchain definitions (ARM bare metal defaults) 18 | # Set the TOOLCHAIN_PATH variable to the path where it is installed 19 | # If it is accessible globally. ie it is in your system path ($PATH) 20 | # then leave it blank. A slash at the end of the path is required 21 | # eg: TOOLCHAIN_PATH = /usr/local/bin/ 22 | TOOLCHAIN_PATH = 23 | TOOLCHAIN = $(TOOLCHAIN_PATH)arm-none-eabi- 24 | CC = $(TOOLCHAIN)gcc 25 | AS = $(TOOLCHAIN)as 26 | LD = $(TOOLCHAIN)ld 27 | OC = $(TOOLCHAIN)objcopy 28 | OD = $(TOOLCHAIN)objdump 29 | OS = $(TOOLCHAIN)size 30 | 31 | ###################################################################### 32 | 33 | # Assembly directives. 34 | ASFLAGS += -c 35 | ASFLAGS += -O0 36 | ASFLAGS += -mcpu=$(MCU_SPEC) 37 | ASFLAGS += -mthumb 38 | ASFLAGS += -Wall 39 | # (Set error messages to appear on a single line.) 40 | ASFLAGS += -fmessage-length=0 41 | 42 | # C compilation directives 43 | CFLAGS += -mcpu=$(MCU_SPEC) 44 | CFLAGS += -mthumb 45 | CFLAGS += -Wall 46 | CFLAGS += -g 47 | # (Set error messages to appear on a single line.) 48 | CFLAGS += -fmessage-length=0 49 | # (Set system to ignore semihosted junk) 50 | CFLAGS += --specs=nosys.specs 51 | 52 | # Linker directives 53 | LSCRIPT = ./$(LD_SCRIPT) 54 | LFLAGS += -mcpu=$(MCU_SPEC) 55 | LFLAGS += -mthumb 56 | LFLAGS += -Wall 57 | LFLAGS += --specs=nosys.specs 58 | LFLAGS += -nostdlib 59 | LFLAGS += -lgcc 60 | LFLAGS += -T$(LSCRIPT) 61 | 62 | OBJS = $(AS_SRC:.S=.o) 63 | OBJS += $(C_SRC:.c=.o) 64 | 65 | # The PHONY keyword is required so that makefile does not 66 | # consider the rule 'all' as a file 67 | .PHONY: all 68 | all: $(TARGET).bin 69 | 70 | # There should be a tab here on line 71, 4 spaces does not work 71 | %.o: %.S 72 | $(CC) -x assembler-with-cpp $(ASFLAGS) $< -o $@ 73 | 74 | %.o: %.c 75 | $(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@ 76 | 77 | $(TARGET).elf: $(OBJS) 78 | $(CC) $^ $(LFLAGS) -o $@ 79 | 80 | $(TARGET).bin: $(TARGET).elf 81 | $(OC) -S -O binary $< $@ 82 | $(OS) $< 83 | 84 | .PHONY: clean 85 | clean: 86 | rm -f $(OBJS) 87 | rm -f $(TARGET).elf $(TARGET).bin 88 | 89 | -------------------------------------------------------------------------------- /part4-function_call/Makefile: -------------------------------------------------------------------------------- 1 | 2 | #################### CONFIGURABLE SECTION ########################### 3 | # Target can be anything you want. It will create an elf and bin file 4 | # with this name 5 | TARGET = main 6 | 7 | # Define the linker script location and chip architecture. 8 | # Change this based on your linker script name and chip architecture 9 | LD_SCRIPT = stm32_linker.ld 10 | MCU_SPEC = cortex-m4 11 | 12 | # Add assembler and C files to this, later we can add a wildcard 13 | # to compile all c files in the directory 14 | AS_SRC = ./startup.S 15 | C_SRC = ./main.c 16 | 17 | # Toolchain definitions (ARM bare metal defaults) 18 | # Set the TOOLCHAIN_PATH variable to the path where it is installed 19 | # If it is accessible globally. ie it is in your system path ($PATH) 20 | # then leave it blank. A slash at the end of the path is required 21 | # eg: TOOLCHAIN_PATH = /usr/local/bin/ 22 | TOOLCHAIN_PATH = 23 | TOOLCHAIN = $(TOOLCHAIN_PATH)arm-none-eabi- 24 | CC = $(TOOLCHAIN)gcc 25 | AS = $(TOOLCHAIN)as 26 | LD = $(TOOLCHAIN)ld 27 | OC = $(TOOLCHAIN)objcopy 28 | OD = $(TOOLCHAIN)objdump 29 | OS = $(TOOLCHAIN)size 30 | 31 | ###################################################################### 32 | 33 | # Assembly directives. 34 | ASFLAGS += -c 35 | ASFLAGS += -O0 36 | ASFLAGS += -mcpu=$(MCU_SPEC) 37 | ASFLAGS += -mthumb 38 | ASFLAGS += -Wall 39 | # (Set error messages to appear on a single line.) 40 | ASFLAGS += -fmessage-length=0 41 | 42 | # C compilation directives 43 | CFLAGS += -mcpu=$(MCU_SPEC) 44 | CFLAGS += -mthumb 45 | CFLAGS += -Wall 46 | CFLAGS += -g 47 | # (Set error messages to appear on a single line.) 48 | CFLAGS += -fmessage-length=0 49 | # (Set system to ignore semihosted junk) 50 | CFLAGS += --specs=nosys.specs 51 | 52 | # Linker directives 53 | LSCRIPT = ./$(LD_SCRIPT) 54 | LFLAGS += -mcpu=$(MCU_SPEC) 55 | LFLAGS += -mthumb 56 | LFLAGS += -Wall 57 | LFLAGS += --specs=nosys.specs 58 | LFLAGS += -nostdlib 59 | LFLAGS += -lgcc 60 | LFLAGS += -T$(LSCRIPT) 61 | 62 | OBJS = $(AS_SRC:.S=.o) 63 | OBJS += $(C_SRC:.c=.o) 64 | 65 | # The PHONY keyword is required so that makefile does not 66 | # consider the rule 'all' as a file 67 | .PHONY: all 68 | all: $(TARGET).bin 69 | 70 | # There should be a tab here on line 71, 4 spaces does not work 71 | %.o: %.S 72 | $(CC) -x assembler-with-cpp $(ASFLAGS) $< -o $@ 73 | 74 | %.o: %.c 75 | $(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@ 76 | 77 | $(TARGET).elf: $(OBJS) 78 | $(CC) $^ $(LFLAGS) -o $@ 79 | 80 | $(TARGET).bin: $(TARGET).elf 81 | $(OC) -S -O binary $< $@ 82 | $(OS) $< 83 | 84 | .PHONY: clean 85 | clean: 86 | rm -f $(OBJS) 87 | rm -f $(TARGET).elf $(TARGET).bin 88 | 89 | -------------------------------------------------------------------------------- /semihosting/startup.S: -------------------------------------------------------------------------------- 1 | // These instructions define attributes of our chip and 2 | // the assembly language we'll use: 3 | .syntax unified 4 | .cpu cortex-m4 5 | .thumb 6 | 7 | // Global memory locations. 8 | .global vtable 9 | .global reset_handler 10 | 11 | /* 12 | * The actual vector table. 13 | * Only the size of RAM and 'reset' handler are 14 | * included, for simplicity. 15 | */ 16 | .type vtable, %object 17 | /* This is additional line that is required to be added so that 18 | * we create a new section header called vector_table in the object file 19 | * which will be placed in the vector_table section of the output elf by 20 | * the linker */ 21 | .section .vector_table,"a",%progbits 22 | vtable: 23 | .word _estack 24 | .word reset_handler 25 | 26 | /* 27 | * The Reset handler. Called on reset/Power on 28 | */ 29 | .type reset_handler, %function 30 | reset_handler: 31 | // Set the stack pointer to the end of the stack. 32 | // The '_estack' value is defined in our linker script. 33 | LDR r0, =_estack 34 | MOV sp, r0 35 | 36 | // Copy data from flash to RAM data init section. 37 | // R2 will store our progress along the sidata section. 38 | 39 | MOVS r0, #0 40 | 41 | // Load the start/end addresses of the data section, 42 | // and the start of the data init section. 43 | LDR r1, =_sdata 44 | LDR r2, =_edata 45 | LDR r3, =_sidata 46 | B copy_sidata_loop 47 | 48 | copy_sidata: 49 | // Offset the data init section by our copy progress. 50 | // Load from r3 offset by r0 to r4 51 | LDR r4, [r3, r0] 52 | // Copy the current word into data, and increment. 53 | // Store r4 to r1 offset by r0 54 | STR r4, [r1, r0] 55 | ADDS r0, r0, #4 56 | 57 | copy_sidata_loop: 58 | // Unless we've copied the whole data section, copy the 59 | // next word from sidata->data. 60 | ADDS r4, r0, r1 61 | CMP r4, r2 62 | BCC copy_sidata 63 | 64 | // Once we are done copying the data section into RAM, 65 | // move on to filling the BSS section with 0s. 66 | MOVS r0, #0 67 | LDR r1, =_sbss 68 | LDR r2, =_ebss 69 | B reset_bss_loop 70 | 71 | // Fill the BSS segment with '0's. 72 | reset_bss: 73 | // Store a 0 and increment by a word. 74 | STR r0, [r1] 75 | ADDS r1, r1, #4 76 | 77 | reset_bss_loop: 78 | // We'll use R1 to count progress here; if we aren't 79 | // done, reset the next word and increment. 80 | CMP r1, r2 81 | BCC reset_bss 82 | 83 | // For the single pic base option, we need to 84 | // initialize r9 with the GOT base address 85 | LDR r9, =_sgot 86 | 87 | // And then branch off to main 88 | B main 89 | 90 | __main_loop: 91 | // Loop back. 92 | B __main_loop 93 | 94 | .size reset_handler, .-reset_handler 95 | -------------------------------------------------------------------------------- /got_plt/startup.S: -------------------------------------------------------------------------------- 1 | // These instructions define attributes of our chip and 2 | // the assembly language we'll use: 3 | .syntax unified 4 | .cpu cortex-m4 5 | .fpu softvfp 6 | .thumb 7 | 8 | // Global memory locations. 9 | .global vtable 10 | .global reset_handler 11 | 12 | /* 13 | * The actual vector table. 14 | * Only the size of RAM and 'reset' handler are 15 | * included, for simplicity. 16 | */ 17 | .type vtable, %object 18 | /* This is additional line that is required to be added so that 19 | * we create a new section header called vector_table in the object file 20 | * which will be placed in the vector_table section of the output elf by 21 | * the linker */ 22 | .section .vector_table,"a",%progbits 23 | vtable: 24 | .word _estack 25 | .word reset_handler 26 | 27 | /* 28 | * The Reset handler. Called on reset/Power on 29 | */ 30 | .type reset_handler, %function 31 | reset_handler: 32 | // Set the stack pointer to the end of the stack. 33 | // The '_estack' value is defined in our linker script. 34 | LDR r0, =_estack 35 | MOV sp, r0 36 | 37 | // Copy data from flash to RAM data init section. 38 | // R2 will store our progress along the sidata section. 39 | 40 | MOVS r0, #0 41 | 42 | // Load the start/end addresses of the data section, 43 | // and the start of the data init section. 44 | LDR r1, =_sdata 45 | LDR r2, =_edata 46 | LDR r3, =_sidata 47 | B copy_sidata_loop 48 | 49 | copy_sidata: 50 | // Offset the data init section by our copy progress. 51 | // Load from r3 offset by r0 to r4 52 | LDR r4, [r3, r0] 53 | // Copy the current word into data, and increment. 54 | // Store r4 to r1 offset by r0 55 | STR r4, [r1, r0] 56 | ADDS r0, r0, #4 57 | 58 | copy_sidata_loop: 59 | // Unless we've copied the whole data section, copy the 60 | // next word from sidata->data. 61 | ADDS r4, r0, r1 62 | CMP r4, r2 63 | BCC copy_sidata 64 | 65 | // Once we are done copying the data section into RAM, 66 | // move on to filling the BSS section with 0s. 67 | MOVS r0, #0 68 | LDR r1, =_sbss 69 | LDR r2, =_ebss 70 | B reset_bss_loop 71 | 72 | // Fill the BSS segment with '0's. 73 | reset_bss: 74 | // Store a 0 and increment by a word. 75 | STR r0, [r1] 76 | ADDS r1, r1, #4 77 | 78 | reset_bss_loop: 79 | // We'll use R1 to count progress here; if we aren't 80 | // done, reset the next word and increment. 81 | CMP r1, r2 82 | BCC reset_bss 83 | 84 | // For the single pic base option, we need to 85 | // initialize r9 with the GOT base address 86 | LDR r9, =_sgot 87 | 88 | // And then branch off to main 89 | B main 90 | 91 | __main_loop: 92 | // Loop back. 93 | B __main_loop 94 | 95 | .size reset_handler, .-reset_handler 96 | -------------------------------------------------------------------------------- /part3-get_to_main/stm32_linker.ld: -------------------------------------------------------------------------------- 1 | /* Explicitly mention label for the program's entry point */ 2 | ENTRY(reset_handler) 3 | 4 | /* End of RAM / Start of stack */ 5 | /* (4KB SRAM) */ 6 | _estack = 0x20001000; 7 | 8 | /* Set minimum size for stack and dynamic memory. */ 9 | /* (The linker will generate an error if there is 10 | * less than this much RAM leftover.) 11 | * For the current application, 1KB is enough 12 | * but as the complexity grow, you will have to 13 | * increase this */ 14 | /* (1KB) */ 15 | _Min_Leftover_RAM = 0x400; 16 | 17 | MEMORY 18 | { 19 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K 20 | RAM (rxw) : ORIGIN = 0x20000000, LENGTH = 4K 21 | } 22 | 23 | SECTIONS 24 | { 25 | /* The vector table goes at the start of flash. */ 26 | .vector_table : 27 | { 28 | . = ALIGN(4); /* The code memory ie FLASH of ARM Cortex-M micros need to be word aligned */ 29 | /* The word size is 32 bits or 4 bytes for the one i am using */ 30 | KEEP (*(.vector_table)) /* Use KEEP command to avoid removal of code if using link time garbage collection */ 31 | . = ALIGN(4); 32 | } >FLASH 33 | 34 | /* The 'text' section contains the main program code. */ 35 | .text : 36 | { 37 | . = ALIGN(4); 38 | *(.text*) 39 | . = ALIGN(4); 40 | 41 | } >FLASH 42 | 43 | /* The 'rodata' section contains read-only data, 44 | * constants, strings, information that won't change. 45 | * variables defined with const keyword in C */ 46 | .rodata : 47 | { 48 | . = ALIGN(4); 49 | *(.rodata*) 50 | . = ALIGN(4); 51 | 52 | } >FLASH 53 | 54 | /* The 'data' section is space set aside in RAM for 55 | * things like non-zero global and static variables, which can change. */ 56 | 57 | 58 | /* _sidata will be the location .data section will be stored 59 | * in the flash, and _sdata will be the location in the RAM 60 | * We use these two addresses to copy data from _sidata 61 | * to _sdata*/ 62 | _sidata = .; 63 | .data : AT(_sidata) /* The AT defines the load address of the .data section. This is where the section will be loaded */ 64 | { 65 | /* We cannot declare _sidata here, because it will be assigned the 66 | * start of RAM (0x20000000), instead of end of FLASH */ 67 | . = ALIGN(4); 68 | /* Mark start/end locations for the 'data' section. */ 69 | _sdata = .; 70 | *(.data*) 71 | _edata = .; 72 | . = ALIGN(4); 73 | 74 | } >RAM /* Mentioning RAM here, specifies the VMA ie virtual memory address ie address during execution */ 75 | 76 | /* The 'bss' section is similar to the 'data' section, 77 | * but its space is initialized to all 0s at the 78 | * start of the program. */ 79 | .bss : 80 | { 81 | . = ALIGN(4); 82 | /* Also mark the start/end of the BSS section. */ 83 | _sbss = .; 84 | *(.bss*) 85 | /* A special notation is needed for common symbols, 86 | * because in many object file formats common symbols 87 | * do not have a particular input section. The linker 88 | * treats common symbols as though they are in an 89 | * input section named ‘COMMON’.*/ 90 | *(COMMON) 91 | . = ALIGN(4); 92 | _ebss = .; 93 | 94 | } >RAM 95 | 96 | /* Space set aside for the application's heap/stack. */ 97 | .dynamic_allocations : 98 | { 99 | . = ALIGN(4); 100 | _ssystem_ram = .; 101 | . = . + _Min_Leftover_RAM; 102 | . = ALIGN(4); 103 | _esystem_ram = .; 104 | 105 | } >RAM 106 | 107 | } 108 | -------------------------------------------------------------------------------- /part4-function_call/stm32_linker.ld: -------------------------------------------------------------------------------- 1 | /* Explicitly mention label for the program's entry point */ 2 | ENTRY(reset_handler) 3 | 4 | /* End of RAM / Start of stack */ 5 | /* (4KB SRAM) */ 6 | _estack = 0x20001000; 7 | 8 | /* Set minimum size for stack and dynamic memory. */ 9 | /* (The linker will generate an error if there is 10 | * less than this much RAM leftover.) 11 | * For the current application, 1KB is enough 12 | * but as the complexity grow, you will have to 13 | * increase this */ 14 | /* (1KB) */ 15 | _Min_Leftover_RAM = 0x400; 16 | 17 | MEMORY 18 | { 19 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K 20 | RAM (rxw) : ORIGIN = 0x20000000, LENGTH = 4K 21 | } 22 | 23 | SECTIONS 24 | { 25 | /* The vector table goes at the start of flash. */ 26 | .vector_table : 27 | { 28 | . = ALIGN(4); /* The code memory ie FLASH of ARM Cortex-M micros need to be word aligned */ 29 | /* The word size is 32 bits or 4 bytes for the one i am using */ 30 | KEEP (*(.vector_table)) /* Use KEEP command to avoid removal of code if using link time garbage collection */ 31 | . = ALIGN(4); 32 | } >FLASH 33 | 34 | /* The 'text' section contains the main program code. */ 35 | .text : 36 | { 37 | . = ALIGN(4); 38 | *(.text*) 39 | . = ALIGN(4); 40 | 41 | } >FLASH 42 | 43 | /* The 'rodata' section contains read-only data, 44 | * constants, strings, information that won't change. 45 | * variables defined with const keyword in C */ 46 | .rodata : 47 | { 48 | . = ALIGN(4); 49 | *(.rodata*) 50 | . = ALIGN(4); 51 | 52 | } >FLASH 53 | 54 | /* The 'data' section is space set aside in RAM for 55 | * things like non-zero global and static variables, which can change. */ 56 | 57 | 58 | /* _sidata will be the location .data section will be stored 59 | * in the flash, and _sdata will be the location in the RAM 60 | * We use these two addresses to copy data from _sidata 61 | * to _sdata*/ 62 | _sidata = .; 63 | .data : AT(_sidata) /* The AT defines the load address of the .data section. This is where the section will be loaded */ 64 | { 65 | /* We cannot declare _sidata here, because it will be assigned the 66 | * start of RAM (0x20000000), instead of end of FLASH */ 67 | . = ALIGN(4); 68 | /* Mark start/end locations for the 'data' section. */ 69 | _sdata = .; 70 | *(.data*) 71 | _edata = .; 72 | . = ALIGN(4); 73 | 74 | } >RAM /* Mentioning RAM here, specifies the VMA ie virtual memory address ie address during execution */ 75 | 76 | /* The 'bss' section is similar to the 'data' section, 77 | * but its space is initialized to all 0s at the 78 | * start of the program. */ 79 | .bss : 80 | { 81 | . = ALIGN(4); 82 | /* Also mark the start/end of the BSS section. */ 83 | _sbss = .; 84 | *(.bss*) 85 | /* A special notation is needed for common symbols, 86 | * because in many object file formats common symbols 87 | * do not have a particular input section. The linker 88 | * treats common symbols as though they are in an 89 | * input section named ‘COMMON’.*/ 90 | *(COMMON) 91 | . = ALIGN(4); 92 | _ebss = .; 93 | 94 | } >RAM 95 | 96 | /* Space set aside for the application's heap/stack. */ 97 | .dynamic_allocations : 98 | { 99 | . = ALIGN(4); 100 | _ssystem_ram = .; 101 | . = . + _Min_Leftover_RAM; 102 | . = ALIGN(4); 103 | _esystem_ram = .; 104 | 105 | } >RAM 106 | 107 | } 108 | -------------------------------------------------------------------------------- /got_plt/stm32_linker.ld: -------------------------------------------------------------------------------- 1 | /* Explicitly mention label for the program's entry point */ 2 | ENTRY(reset_handler) 3 | 4 | /* End of RAM / Start of stack */ 5 | /* (4KB SRAM) */ 6 | _estack = 0x20001000; 7 | 8 | /* Set minimum size for stack and dynamic memory. */ 9 | /* (The linker will generate an error if there is 10 | * less than this much RAM leftover.) 11 | * For the current application, 1KB is enough 12 | * but as the complexity grow, you will have to 13 | * increase this */ 14 | /* (1KB) */ 15 | _Min_Leftover_RAM = 0x400; 16 | 17 | MEMORY 18 | { 19 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K 20 | RAM (rxw) : ORIGIN = 0x20000000, LENGTH = 4K 21 | } 22 | 23 | SECTIONS 24 | { 25 | /* The vector table goes at the start of flash. */ 26 | .vector_table : 27 | { 28 | . = ALIGN(4); /* The code memory ie FLASH of ARM Cortex-M micros need to be word aligned */ 29 | /* The word size is 32 bits or 4 bytes for the one i am using */ 30 | KEEP (*(.vector_table)) /* Use KEEP command to avoid removal of code if using link time garbage collection */ 31 | . = ALIGN(4); 32 | } >FLASH 33 | 34 | /* The 'text' section contains the main program code. */ 35 | .text : 36 | { 37 | . = ALIGN(4); 38 | *(.text*) 39 | . = ALIGN(4); 40 | 41 | } >FLASH 42 | 43 | /* The 'rodata' section contains read-only data, 44 | * constants, strings, information that won't change. 45 | * variables defined with const keyword in C */ 46 | .rodata : 47 | { 48 | . = ALIGN(4); 49 | *(.rodata*) 50 | . = ALIGN(4); 51 | 52 | } >FLASH 53 | 54 | /* PLT section contains code for accessing the dynamically linked functions 55 | * ie funtions from shared libraries in a postion independent manner */ 56 | .plt : 57 | { 58 | . = ALIGN(4); 59 | *(.plt) 60 | . = ALIGN(4); 61 | } >FLASH 62 | 63 | /* The 'data' section is space set aside in RAM for 64 | * things like non-zero global and static variables, which can change. */ 65 | 66 | .data : 67 | { 68 | /* We cannot declare _sidata here, because it will be assigned the 69 | * start of RAM (0x20000000), instead of end of FLASH */ 70 | . = ALIGN(4); 71 | /* Mark start/end locations for the 'data' section. */ 72 | _sdata = .; 73 | *(.data*) 74 | . = ALIGN(4); 75 | 76 | } >RAM AT> FLASH /* Mentioning RAM here, specifies the VMA ie virtual memory address ie address during execution */ 77 | /* The AT defines the load address of the .data section. This is where the section will be loaded */ 78 | 79 | /* _sidata will be the location .data section will be stored 80 | * in the flash, and _sdata will be the location of .data section 81 | * in the RAM. We use these two addresses to copy data from _sidata 82 | * to _sdata*/ 83 | _sidata = LOADADDR(.data); 84 | 85 | /* The global offset table is the table for indirectly accessing the global variables 86 | * The table contains addresses of the global variables. The text section contains 87 | * a address of the GOT base and a offset in it to access the appropriate variables. 88 | * This is done to access the variables in a postion independent manner. */ 89 | .got : 90 | { 91 | . = ALIGN(4); 92 | _sgot = .; 93 | *(.got) 94 | } >RAM AT> FLASH 95 | 96 | /* got.plt section contains entries which is used with the PLT to access the functions 97 | * in a position independent manner. */ 98 | .got.plt : 99 | { 100 | . = ALIGN(4); 101 | *(.got.plt) 102 | _edata = .; 103 | } >RAM AT> FLASH 104 | 105 | /* The 'bss' section is similar to the 'data' section, 106 | * but its space is initialized to all 0s at the 107 | * start of the program. */ 108 | .bss : 109 | { 110 | . = ALIGN(4); 111 | /* Also mark the start/end of the BSS section. */ 112 | _sbss = .; 113 | *(.bss*) 114 | /* A special notation is needed for common symbols, 115 | * because in many object file formats common symbols 116 | * do not have a particular input section. The linker 117 | * treats common symbols as though they are in an 118 | * input section named ‘COMMON’.*/ 119 | *(COMMON) 120 | . = ALIGN(4); 121 | _ebss = .; 122 | 123 | } >RAM 124 | 125 | /* Space set aside for the application's heap/stack. */ 126 | .dynamic_allocations : 127 | { 128 | . = ALIGN(4); 129 | _ssystem_ram = .; 130 | . = . + _Min_Leftover_RAM; 131 | . = ALIGN(4); 132 | _esystem_ram = .; 133 | 134 | } >RAM 135 | 136 | } 137 | -------------------------------------------------------------------------------- /semihosting/stm32_linker.ld: -------------------------------------------------------------------------------- 1 | /* Explicitly mention label for the program's entry point */ 2 | ENTRY(reset_handler) 3 | 4 | /* End of RAM / Start of stack */ 5 | /* (4KB SRAM) */ 6 | _estack = 0x20001000; 7 | 8 | /* Set minimum size for stack and dynamic memory. */ 9 | /* (The linker will generate an error if there is 10 | * less than this much RAM leftover.) 11 | * For the current application, 1KB is enough 12 | * but as the complexity grow, you will have to 13 | * increase this */ 14 | /* (1KB) */ 15 | _Min_Leftover_RAM = 0x400; 16 | 17 | MEMORY 18 | { 19 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 20 | CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K 21 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K 22 | } 23 | 24 | SECTIONS 25 | { 26 | /* The vector table goes at the start of flash. */ 27 | .vector_table : 28 | { 29 | . = ALIGN(4); /* The code memory ie FLASH of ARM Cortex-M micros need to be word aligned */ 30 | /* The word size is 32 bits or 4 bytes for the one i am using */ 31 | KEEP (*(.vector_table)) /* Use KEEP command to avoid removal of code if using link time garbage collection */ 32 | . = ALIGN(4); 33 | } >FLASH 34 | 35 | /* The 'text' section contains the main program code. */ 36 | .text : 37 | { 38 | . = ALIGN(4); 39 | *(.text*) 40 | . = ALIGN(4); 41 | 42 | } >FLASH 43 | 44 | /* The 'rodata' section contains read-only data, 45 | * constants, strings, information that won't change. 46 | * variables defined with const keyword in C */ 47 | .rodata : 48 | { 49 | . = ALIGN(4); 50 | *(.rodata*) 51 | . = ALIGN(4); 52 | 53 | } >FLASH 54 | 55 | /* PLT section contains code for accessing the dynamically linked functions 56 | * ie funtions from shared libraries in a postion independent manner */ 57 | .plt : 58 | { 59 | . = ALIGN(4); 60 | *(.plt) 61 | . = ALIGN(4); 62 | } >FLASH 63 | 64 | /* The 'data' section is space set aside in RAM for 65 | * things like non-zero global and static variables, which can change. */ 66 | 67 | .data : 68 | { 69 | /* We cannot declare _sidata here, because it will be assigned the 70 | * start of RAM (0x20000000), instead of end of FLASH */ 71 | . = ALIGN(4); 72 | /* Mark start/end locations for the 'data' section. */ 73 | _sdata = .; 74 | *(.data*) 75 | . = ALIGN(4); 76 | 77 | } >RAM AT> FLASH /* Mentioning RAM here, specifies the VMA ie virtual memory address ie address during execution */ 78 | /* The AT defines the load address of the .data section. This is where the section will be loaded */ 79 | 80 | /* _sidata will be the location .data section will be stored 81 | * in the flash, and _sdata will be the location of .data section 82 | * in the RAM. We use these two addresses to copy data from _sidata 83 | * to _sdata*/ 84 | _sidata = LOADADDR(.data); 85 | 86 | /* The global offset table is the table for indirectly accessing the global variables 87 | * The table contains addresses of the global variables. The text section contains 88 | * a address of the GOT base and a offset in it to access the appropriate variables. 89 | * This is done to access the variables in a postion independent manner. */ 90 | .got : 91 | { 92 | . = ALIGN(4); 93 | _sgot = .; 94 | *(.got) 95 | } >RAM AT> FLASH 96 | 97 | /* got.plt section contains entries which is used with the PLT to access the functions 98 | * in a position independent manner. */ 99 | .got.plt : 100 | { 101 | . = ALIGN(4); 102 | *(.got.plt) 103 | _edata = .; 104 | } >RAM AT> FLASH 105 | 106 | /* The 'bss' section is similar to the 'data' section, 107 | * but its space is initialized to all 0s at the 108 | * start of the program. */ 109 | .bss : 110 | { 111 | . = ALIGN(4); 112 | /* Also mark the start/end of the BSS section. */ 113 | _sbss = .; 114 | __bss_start__ = _sbss; 115 | *(.bss*) 116 | /* A special notation is needed for common symbols, 117 | * because in many object file formats common symbols 118 | * do not have a particular input section. The linker 119 | * treats common symbols as though they are in an 120 | * input section named ‘COMMON’.*/ 121 | *(COMMON) 122 | . = ALIGN(4); 123 | _ebss = .; 124 | __bss_end__ = _ebss; 125 | 126 | } >RAM 127 | 128 | /* Space set aside for the application's heap/stack. */ 129 | .dynamic_allocations : 130 | { 131 | . = ALIGN(4); 132 | PROVIDE ( end = . ); 133 | PROVIDE ( _end = . ); 134 | PROVIDE ( __end__ = . ); 135 | _ssystem_ram = .; 136 | . = . + _Min_Leftover_RAM; 137 | . = ALIGN(4); 138 | _esystem_ram = .; 139 | 140 | } >RAM 141 | } 142 | -------------------------------------------------------------------------------- /got_plt/Makefile: -------------------------------------------------------------------------------- 1 | 2 | #################### CONFIGURABLE SECTION ########################### 3 | # Target can be anything you want. It will create an elf and bin file 4 | # with this name 5 | TARGET = main 6 | 7 | # Define the linker script location and chip architecture. 8 | # Change this based on your linker script name and chip architecture 9 | LD_SCRIPT = stm32_linker.ld 10 | MCU_SPEC = cortex-m4 11 | 12 | # Add assembler and C files to this, later we can add a wildcard 13 | # to compile all c files in the directory 14 | AS_SRC = startup.S 15 | C_SRC = main.c 16 | LIBS_SRC = 17 | DLIB_SRC = mylib.c 18 | # Toolchain definitions (ARM bare metal defaults) 19 | # Set the TOOLCHAIN_PATH variable to the path where it is installed 20 | # If it is accessible globally. ie it is in your system path ($PATH) 21 | # then leave it blank. A slash at the end of the path is required 22 | # eg: TOOLCHAIN_PATH = /usr/local/bin/ 23 | TOOLCHAIN_PATH = 24 | TOOLCHAIN = $(TOOLCHAIN_PATH)arm-none-eabi- 25 | CC = $(TOOLCHAIN)gcc 26 | AS = $(TOOLCHAIN)as 27 | LD = $(TOOLCHAIN)ld 28 | OC = $(TOOLCHAIN)objcopy 29 | OD = $(TOOLCHAIN)objdump 30 | OS = $(TOOLCHAIN)size 31 | GDB = $(TOOLCHAIN)gdb 32 | 33 | ###################################################################### 34 | .DELETE_ON_ERROR: 35 | 36 | # Assembly directives. 37 | ASFLAGS += -c 38 | ASFLAGS += -O0 39 | ASFLAGS += -mcpu=$(MCU_SPEC) 40 | ASFLAGS += -mthumb 41 | ASFLAGS += -Wall 42 | # (Set error messages to appear on a single line.) 43 | ASFLAGS += -fmessage-length=0 44 | 45 | # C compilation directives 46 | CFLAGS += -c 47 | CFLAGS += -mcpu=$(MCU_SPEC) 48 | CFLAGS += -mthumb 49 | CFLAGS += -Wall 50 | CFLAGS += -g 51 | # (Set error messages to appear on a single line.) 52 | CFLAGS += -fmessage-length=0 53 | # (Set system to ignore semihosted junk) 54 | CFLAGS += --specs=nosys.specs 55 | 56 | # Linker directives 57 | LSCRIPT = ./$(LD_SCRIPT) 58 | 59 | LFLAGS += -g 60 | LFLAGS += -nostdlib 61 | LFLAGS += -T$(LSCRIPT) 62 | LFLAGS += -Map $(TARGET).map 63 | LFLAGS += --print-memory-usage 64 | 65 | OBJS += $(AS_SRC:.S=.o) 66 | OBJS += $(C_SRC:.c=.o) 67 | OBJS += $(LIB_SRC:.c=.o) 68 | 69 | DLIB_CFLAGS += -mcpu=$(MCU_SPEC) 70 | DLIB_CFLAGS += -mthumb 71 | DLIB_CFLAGS += -Wall 72 | DLIB_CFLAGS += -g 73 | DLIB_CFLAGS += -fPIC 74 | DLIB_CFLAGS += -fmessage-length=0 75 | DLIB_CFLAGS += --specs=nosys.specs 76 | 77 | DLIB_LFLAGS += $(DLIB_SRC:%.c=-l%) 78 | DLIBS += $(DLIB_SRC:%.c=lib%.so) 79 | 80 | # The following two lines are to remove the existing so and o files 81 | DLIBS_SO += $(DLIB_SRC:.c=.so) 82 | DLIBS_O += $(DLIB_SRC:.c=.o) 83 | 84 | # The PHONY keyword is required so that makefile does not 85 | # consider the rule 'all' as a file 86 | .PHONY: all 87 | all: $(TARGET).bin 88 | 89 | # There should be a tab here on the line with $(CC), 4 spaces does not work 90 | %.o: %.S 91 | $(CC) -x assembler-with-cpp $(ASFLAGS) $< -o $@ 92 | 93 | %.o: %.s 94 | $(CC) -x assembler-with-cpp $(ASFLAGS) $< -o $@ 95 | 96 | # If -c is used then it will create a reloc file ie normal object file 97 | # and not a dynamic object. For dynamic object -shared is required. 98 | %.o: %.c 99 | $(CC) $(CFLAGS) $(INCLUDE) $< -o $@ 100 | 101 | %.so: %.c 102 | $(CC) -shared $(DLIB_CFLAGS) $< -o lib$@ 103 | 104 | $(TARGET).diss: $(TARGET).elf 105 | $(OD) -Dz --source $^ > $@ 106 | 107 | $(TARGET).elf: $(OBJS) 108 | $(LD) $^ $(LFLAGS) -o $@ 109 | 110 | $(TARGET).bin: $(TARGET).elf $(TARGET).diss 111 | $(OC) -S -O binary $< $@ 112 | $(OS) $< 113 | 114 | ########################### got_only ########################### 115 | # @Brief This target makes executable with global variables 116 | # accessed through GOT while procedure calls are direct 117 | got_only:ASFLAGS += -fPIC 118 | got_only:CFLAGS += -fPIC 119 | got_only:LFLAGS += $(DLIBS_O) 120 | .PHONY: got_only 121 | got_only: $(DLIBS_O) $(TARGET).bin 122 | ################################################################ 123 | 124 | ########################### got_plt ############################ 125 | # @Brief This target makes executable with global variables 126 | # accessed through GOT and shared library functions through 127 | # the PLT 128 | got_plt:ASFLAGS += -fPIC 129 | got_plt:CFLAGS += -fPIC 130 | got_plt:LFLAGS += -L. $(DLIB_LFLAGS) 131 | .PHONY: got_plt 132 | got_plt: $(DLIBS_SO) $(TARGET).bin 133 | ################################################################ 134 | 135 | ####################### got_plt_single ######################### 136 | # @Brief This target makes executable with global variables 137 | # accessed through GOT and shared library functions through 138 | # the PLT. The GOT base is hardcoded in register r9. 139 | got_plt_single:ASFLAGS += -fPIC -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative 140 | got_plt_single:CFLAGS += -fPIC -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative 141 | got_plt_single:DLIB_CFLAGS += -fPIC -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative 142 | got_plt_single:LFLAGS += -L. $(DLIB_LFLAGS) 143 | .PHONY: got_plt_single 144 | got_plt_single: $(DLIBS_SO) $(TARGET).bin 145 | ################################################################ 146 | 147 | ########################### clean ############################## 148 | # @Brief Remove the target output files. 149 | .PHONY: clean 150 | clean: 151 | rm -f $(OBJS) $(DLIBS) $(DLIBS_O) 152 | rm -f $(TARGET).elf $(TARGET).bin $(TARGET).list $(TARGET).srec 153 | rm -f $(TARGET).diss $(TARGET).map 154 | ################################################################ 155 | 156 | ########################### gdb ################################ 157 | # @Brief Start GDB, connect to server and load the elf 158 | .PHONY: gdb 159 | gdb: 160 | @pgrep -x "st-util" || (echo "Please start st-util"; exit -1) 161 | @echo "Starting GDB" 162 | $(GDB) -ex "target extended localhost:4242" -ex "load $(TARGET).elf" $(TARGET).elf 163 | ################################################################ 164 | -------------------------------------------------------------------------------- /semihosting/Makefile: -------------------------------------------------------------------------------- 1 | 2 | #################### CONFIGURABLE SECTION ########################### 3 | # Target can be anything you want. It will create an elf and bin file 4 | # with this name 5 | TARGET = main 6 | 7 | # Define the linker script location and chip architecture. 8 | # Change this based on your linker script name and chip architecture 9 | LD_SCRIPT = stm32_linker.ld 10 | MCU_SPEC = cortex-m4 11 | FLOAT_SPEC = -mfloat-abi=hard -mfpu=fpv4-sp-d16 12 | 13 | # Add assembler and C files to this, later we can add a wildcard 14 | # to compile all c files in the directory 15 | AS_SRC = startup.S 16 | C_SRC = main.c 17 | LIBS_SRC = 18 | DLIB_SRC = mylib.c 19 | # Toolchain definitions (ARM bare metal defaults) 20 | # Set the TOOLCHAIN_PATH variable to the path where it is installed 21 | # If it is accessible globally. ie it is in your system path ($PATH) 22 | # then leave it blank. A slash at the end of the path is required 23 | # eg: TOOLCHAIN_PATH = /usr/local/bin/ 24 | TOOLCHAIN_PATH = 25 | TOOLCHAIN = $(TOOLCHAIN_PATH)arm-none-eabi- 26 | CC = $(TOOLCHAIN)gcc 27 | AS = $(TOOLCHAIN)as 28 | LD = $(TOOLCHAIN)gcc 29 | OC = $(TOOLCHAIN)objcopy 30 | OD = $(TOOLCHAIN)objdump 31 | OS = $(TOOLCHAIN)size 32 | GDB = $(TOOLCHAIN)gdb 33 | 34 | ###################################################################### 35 | .DELETE_ON_ERROR: 36 | 37 | # Assembly directives. 38 | ASFLAGS += -O0 39 | ASFLAGS += -mcpu=$(MCU_SPEC) 40 | ASFLAGS += -mthumb 41 | ASFLAGS += -mthumb-interwork 42 | ASFLAGS += -Wall 43 | # (Set error messages to appear on a single line.) 44 | ASFLAGS += -fmessage-length=0 45 | ASFLAGS += $(FLOAT_SPEC) 46 | 47 | # C compilation directives 48 | CFLAGS += -mcpu=$(MCU_SPEC) 49 | CFLAGS += -mthumb 50 | CFLAGS += -mthumb-interwork 51 | CFLAGS += -Wall 52 | CFLAGS += -g3 53 | CFLAGS += $(FLOAT_SPEC) 54 | # (Set error messages to appear on a single line.) 55 | CFLAGS += -fmessage-length=0 56 | # Create separate sections for function and data 57 | # so it can be garbage collected by linker 58 | CFLAGS += -ffunction-sections 59 | CFLAGS += -fdata-sections 60 | 61 | # Linker directives 62 | LSCRIPT = ./$(LD_SCRIPT) 63 | 64 | LFLAGS += $(CFLAGS) 65 | #~ LFLAGS += -nostdlib 66 | LFLAGS += -T$(LSCRIPT) 67 | LFLAGS += -Wl,-Map=$(TARGET).map 68 | LFLAGS += -Wl,--print-memory-usage 69 | LFLAGS += -Wl,--gc-sections 70 | 71 | OBJS += $(AS_SRC:.S=.o) 72 | OBJS += $(C_SRC:.c=.o) 73 | OBJS += $(LIB_SRC:.c=.o) 74 | 75 | DLIB_CFLAGS += -mcpu=$(MCU_SPEC) 76 | DLIB_CFLAGS += -mthumb 77 | DLIB_CFLAGS += -Wall 78 | DLIB_CFLAGS += -g3 79 | DLIB_CFLAGS += -fPIC 80 | DLIB_CFLAGS += -fmessage-length=0 81 | 82 | DLIB_LFLAGS += $(DLIB_SRC:%.c=-l%) 83 | DLIBS += $(DLIB_SRC:%.c=lib%.so) 84 | 85 | # The following two lines are to remove the existing so and o files 86 | DLIBS_SO += $(DLIB_SRC:.c=.so) 87 | DLIBS_O += $(DLIB_SRC:.c=.o) 88 | 89 | # The PHONY keyword is required so that makefile does not 90 | # consider the rule 'all' as a file 91 | .PHONY: all 92 | all: $(TARGET).bin 93 | 94 | # There should be a tab here on the line with $(CC), 4 spaces does not work 95 | %.o: %.S 96 | $(CC) -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ 97 | 98 | %.o: %.s 99 | $(CC) -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ 100 | 101 | # If -c is used then it will create a reloc file ie normal object file 102 | # and not a dynamic object. For dynamic object -shared is required. 103 | %.o: %.c 104 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ 105 | 106 | %.so: %.c 107 | $(CC) -shared $(DLIB_CFLAGS) $< -o lib$@ 108 | 109 | $(TARGET).diss: $(TARGET).elf 110 | $(OD) -Dz --source $^ > $@ 111 | 112 | $(TARGET).elf: $(OBJS) 113 | $(LD) $^ $(LFLAGS) -o $@ 114 | 115 | $(TARGET).bin: $(TARGET).elf $(TARGET).diss 116 | $(OC) -S -O binary $< $@ 117 | $(OS) $< 118 | 119 | ########################### no_semihosting #################### 120 | # @Brief This target makes executable with no semihosting 121 | no_semihosting:CFLAGS += --specs=nosys.specs 122 | .PHONY: no_semihosting 123 | no_semihosting: $(TARGET).bin 124 | ################################################################ 125 | 126 | ########################### simple_semihosting ################# 127 | # @Brief This target makes executable with simple demonstration 128 | # of semihosting 129 | simple_semihosting:CFLAGS += -DENABLE_SEMIHOSTING=1 130 | simple_semihosting:LFLAGS += --specs=rdimon.specs -lc -lrdimon 131 | .PHONY: simple_semihosting 132 | simple_semihosting: $(TARGET).bin 133 | ################################################################ 134 | 135 | ####################### pic_semihosting ######################## 136 | # @Brief This target makes executable with global variables 137 | # accessed through GOT and shared library functions through 138 | # the PLT. The GOT base is hardcoded in register r9. 139 | pic_semihosting:ASFLAGS += -fPIC -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -mlong-calls 140 | pic_semihosting:CFLAGS += -DENABLE_SEMIHOSTING=1 -DENABLE_PIC=1 141 | pic_semihosting:CFLAGS += -fPIC -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -mlong-calls 142 | pic_semihosting:DLIB_CFLAGS += -fPIC -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -mlong-calls 143 | pic_semihosting:LFLAGS += --specs=rdimon.specs -lc -lrdimon 144 | pic_semihosting:LFLAGS += -L. $(DLIB_LFLAGS) 145 | .PHONY: pic_semihosting 146 | pic_semihosting: $(DLIBS_SO) $(TARGET).bin 147 | ################################################################ 148 | 149 | ########################### clean ############################## 150 | # @Brief Remove the target output files. 151 | .PHONY: clean 152 | clean: 153 | rm -f $(OBJS) $(DLIBS) $(DLIBS_O) 154 | rm -f $(TARGET).elf $(TARGET).bin $(TARGET).list $(TARGET).srec 155 | rm -f $(TARGET).diss $(TARGET).map 156 | ################################################################ 157 | 158 | ########################### flash ############################## 159 | # @Brief Start GDB, connect to server and load the elf 160 | .PHONY: flash 161 | flash: 162 | @pgrep -x "openocd" || (echo "Please start openocd"; exit -1) 163 | @echo "Starting GDB client" 164 | $(GDB) -ex "target extended :3333" -ex "load $(TARGET).elf" -ex "monitor arm semihosting enable" $(TARGET).elf 165 | ################################################################ 166 | -------------------------------------------------------------------------------- /got_plt/README.md: -------------------------------------------------------------------------------- 1 | ## Part 4 2 | ### Global offset tables and procedure linkage tables 3 | 4 | 5 | 6 | #### Commands to build: 7 | - ``make`` will make statically linked position dependent code, it contains absolute adress to all data and functions 8 | - ``make got_only`` will make statically linked code with global and static data access through the GOT. 9 | - ``make got_plt`` will make code with global and static data access through the GOT and function calls through PLT. 10 | This code will not have the ``library_function`` in the output. 11 | - ``make got_plt_single`` will make got_plt code but with a single base register which is set manually (ie. not by compiler) 12 | prior to calling main. 13 | 14 | #### How GOT works 15 | - use ``make got_only`` to generate GOT. 16 | ``` 17 | arm-none-eabi-objdump -Dz --source main.elf 18 | 19 | int main ( void ) 20 | { 21 | 8000050: b598 push {r3, r4, r7, lr} 22 | 8000052: af00 add r7, sp, #0 23 | 8000054: 4c09 ldr r4, [pc, #36] ; (800007c ) 24 | 8000056: 447c add r4, pc 25 | x=7; 26 | 8000058: 4b09 ldr r3, [pc, #36] ; (8000080 ) 27 | 800005a: 58e3 ldr r3, [r4, r3] 28 | 800005c: 461a mov r2, r3 29 | 800005e: 2307 movs r3, #7 30 | 8000060: 6013 str r3, [r2, #0] 31 | . 32 | . 33 | . 34 | z=library_function(77); 35 | 800006c: 204d movs r0, #77 ; 0x4d 36 | 800006e: f000 f80d bl 800008c 37 | . 38 | . 39 | . 40 | 800007c: 17ffffa6 ldrbne pc, [pc, r6, lsr #31]! ; 41 | 8000080: 00000000 andeq r0, r0, r0 42 | 8000084: 00000008 andeq r0, r0, r8 43 | 8000088: 00000004 andeq r0, r0, r4 44 | . 45 | . 46 | . 47 | Disassembly of section .got: 48 | 49 | 20000000 <.got>: 50 | 20000000: 20000018 andcs r0, r0, r8, lsl r0 51 | 20000004: 2000001c andcs r0, r0, ip, lsl r0 52 | 20000008: 20000020 andcs r0, r0, r0, lsr #32 53 | . 54 | . 55 | . 56 | Disassembly of section .bss: 57 | 58 | 20000018 : 59 | unsigned int x; 60 | 20000018: 00000000 andeq r0, r0, r0 61 | 62 | ``` 63 | The instructions at ``8000054`` and ``8000056`` will load the address of the GOT into register r4 and then the subsequent 64 | access to global data will be as offsets in the GOT. The offsets will be added to the value in the register r4 as shown below. 65 | 66 | | Intruction | Description | 67 | | ---------- | ----------- | 68 | | 8000054 | load data at address ``800007c`` into r4. (pc (instruction address + 4) + #36(0x24) = 800007c) so r4 contains ``17ffffa6`` | 69 | | 8000056 | add pc to r4, so r4 contains ``20000000``, which is the base of the GOT | 70 | | 8000058 | r3 contains data at ``8000080`` ie ``00000000`` which is the offset of x in the GOT | 71 | | 800005a | load data at r4+r3 into r3. ie load data at (20000000+00000000) which is ``20000018`` into r3. | 72 | | 800005c | r2 contains ``20000018`` | 73 | | 800005e | r3 contains #7 | 74 | | 8000060 | store data in r3 to r2+0 ie ``20000018`` | 75 | 76 | The call to ``library_function`` is a direct branch call to its address. 77 | 78 | The compiled objects like main.o contains the relocation information [3] which will be used by the linker to patch the GOT entries. 79 | 80 | ``` 81 | arm-none-eabi-readelf -r main.o 82 | 83 | Relocation section '.rel.text' at offset 0x4fc contains 5 entries: 84 | Offset Info Type Sym.Value Sym. Name 85 | 0000001e 0000130a R_ARM_THM_CALL 00000000 library_function 86 | 0000002c 00001419 R_ARM_BASE_PREL 00000000 _GLOBAL_OFFSET_TABLE_ 87 | 00000030 00000f1a R_ARM_GOT_BREL 00000004 x 88 | 00000034 0000101a R_ARM_GOT_BREL 00000004 y 89 | 00000038 0000111a R_ARM_GOT_BREL 00000004 z 90 | 91 | ``` 92 | 93 | The offset here matches the offset in the disassembly which just contains zeros. See offset 1e, 2c,30,34 and 38 below. 94 | The call to ``library_function`` is a direct call to its address. (which is also 0 and to be patched by linker) 95 | 96 | 97 | ``` 98 | arm-none-eabi-objdump -Dz --source main.o 99 | 100 | Disassembly of section .text: 101 | 102 | 00000000
: 103 | unsigned int x; 104 | unsigned int y; 105 | unsigned int z; 106 | 107 | int main ( void ) 108 | { 109 | 0: b598 push {r3, r4, r7, lr} 110 | 111 | . 112 | . 113 | . 114 | z=library_function(77); 115 | 1c: 204d movs r0, #77 ; 0x4d 116 | 1e: f7ff fffe bl 0 117 | . 118 | . 119 | . 120 | while(1){ 121 | 2a: e7fe b.n 2a 122 | 2c: 00000022 andeq r0, r0, r2, lsr #32 123 | 30: 00000000 andeq r0, r0, r0 124 | 34: 00000000 andeq r0, r0, r0 125 | 38: 00000000 andeq r0, r0, r0 126 | 127 | ``` 128 | 129 | 130 | ### How PLT works 131 | - use ``make got_plt`` to generate PIC code with GOT and PLT. 132 | - In this case ``library_function`` will not get linked into the final binary. ``mylib.c`` is compiled as a shared library and linked. 133 | 134 | ``` 135 | arm-none-eabi-objdump -Dz --source main.elf 136 | 137 | int main ( void ) 138 | { 139 | 8000058: b598 push {r3, r4, r7, lr} 140 | 800005a: af00 add r7, sp, #0 141 | 800005c: 4c09 ldr r4, [pc, #36] ; (8000084 ) 142 | 800005e: 447c add r4, pc 143 | . 144 | . 145 | . 146 | z=library_function(77); 147 | 8000074: 204d movs r0, #77 ; 0x4d 148 | 8000076: f000 f855 bl 8000124 <.plt+0x10> 149 | 800007a: 4602 mov r2, r0 150 | 800007c: 4b04 ldr r3, [pc, #16] ; (8000090 ) 151 | 800007e: 58e3 ldr r3, [r4, r3] 152 | 8000080: 601a str r2, [r3, #0] 153 | 154 | while(1){ 155 | 8000082: e7fe b.n 8000082 156 | 8000084: 18000026 stmdane r0, {r1, r2, r5} 157 | 8000088: 00000000 andeq r0, r0, r0 158 | 800008c: 00000008 andeq r0, r0, r8 159 | 8000090: 00000004 andeq r0, r0, r4 160 | . 161 | . 162 | . 163 | Disassembly of section .plt: 164 | 165 | 08000114 <.plt>: 166 | 8000114: b500 push {lr} 167 | 8000116: f8df e008 ldr.w lr, [pc, #8] ; 8000120 <.plt+0xc> 168 | 800011a: 44fe add lr, pc 169 | 800011c: f85e ff08 ldr.w pc, [lr, #8]! 170 | 8000120: 17ffff74 ; instruction: 0x17ffff74 171 | 8000124: f64f 7c70 movw ip, #65392 ; 0xff70 172 | 8000128: f2c1 7cff movt ip, #6143 ; 0x17ff 173 | 800012c: 44fc add ip, pc 174 | 800012e: f8dc f000 ldr.w pc, [ip] 175 | 8000132: e7fd b.n 8000130 <.plt+0x1c> 176 | . 177 | . 178 | . 179 | Disassembly of section .got.plt: 180 | 181 | 20000094 <_GLOBAL_OFFSET_TABLE_>: 182 | 20000094: 20000000 andcs r0, r0, r0 183 | 20000098: 00000000 andeq r0, r0, r0 184 | 2000009c: 00000000 andeq r0, r0, r0 185 | 200000a0: 08000114 stmdaeq r0, {r2, r4, r8} 186 | ``` 187 | | Intruction | Description | 188 | | ---------- | ----------- | 189 | | 800005c | load data at address ``8000084`` into r4. (pc (instruction address + 4) + #36(0x24) = 8000084) so r4 contains ``18000026`` | 190 | | 800005e | add pc to r4, so r4 contains ``20000088``, which is the base of the GOT | 191 | | 8000074 | move value #77 into r0 which is passed as argument to the library_function. | 192 | | 8000076 | branch to address ``8000124``, which is inside the PLT | 193 | | 8000124 | move ``0xff70`` to the lower 16 bits of ip | 194 | | 8000128 | move ``0x17ff`` to the upper 16 bits of ip, so IP contains ``17ffff78`` | 195 | | 800012c | add ``800012c+0x4``(pc) + ``17ffff70``(ip) = ``200000A0``. which is the GOT for the PLT. This entry will be updated with the actual address by the dynamic linker | 196 | | 800012e | branch to the value at address ``200000A0``, which is initially ``08000114``. This address is the first entry in the PLT which contains code for *lazy binding* and resolving the actual address of the function called. *The value ``08000114`` is likely wrong here, more on this below.*| 197 | | 8000114-800011c | *This part gives a fault* but if it didnt then it would branch to the value at address ``2000009a``, which contains ``00000000`` currently. | 198 | 199 | 200 | - The problem with ``08000114``: 201 | 202 | In ARM cortex-M architecture when interworking addresses (bx & blx or ldr & ldm when loading a pc-relative value) are used for branches 203 | the lowest bit is to be set to 1, to indicate thumb state else a INVSTATE fault will be generated [7]. The generated instruction ``08000114`` 204 | has its last bit as 0 which causes a fault at instruction ``08000114``. This maybe a bug in the gcc compiler [8] [9]. If I change ``08000114`` 205 | to ``08000115`` using a hex editor then the branch works and we end up at ``00000000``. 206 | 207 | 208 | ### Single PIC base register 209 | - use ``make got_plt_single`` to generate PIC code with GOT and PLT, but the register used for the GOT base is the same and constant. 210 | - The GOT base register defined by us needs to be initialized before calling the main function. See the startup.S file for this. 211 | 212 | | Single pic base | Normal | 213 | | ---------- | ----------- | 214 | | int main ( void ) | int main ( void ) | 215 | | { | { | 216 | | 8000050: b580 push {r7, lr} | 8000050: b598 push {r3, r4, r7, lr} | 217 | | 8000052: af00 add r7, sp, #0 | 8000052: af00 add r7, sp, #0 | 218 | | x=7; | 8000054: 4c09 ldr r4, [pc, #36] ; (800007c ) | 219 | | 8000054: 4b0a ldr r3, [pc, #40] ; (8000080 ) | 8000056: 447c add r4, pc | 220 | | 8000056: f859 3003 ldr.w r3, [r9, r3] | x=7; | 221 | 222 | > -msingle-pic-base 223 | > Treat the register used for PIC addressing as read-only, rather than loading it in the prologue for each function. 224 | > The runtime system is responsible for initializing this register with an appropriate value before execution begins. 225 | 226 | 227 | ### Truly position independent code for accessing PLT 228 | - The code we created still accesses the PLT in a PC-relative way. Generation of PIC which accesses PLT through a offset to a base register is 229 | not supported according to [11] [12]. 230 | 231 | ### Notes 232 | - In addition to the change in output sections in the linker script, the method for getting the ``_sidata`` global variable is changed. 233 | ``_sidata`` is the LMA of the data section. This new method gives the correct LMA. If the previous method was used then there was another section 234 | (rel.dyn) being pushed in between the ``_sidata`` and the actual start of the .data section so there was always an offset in accessing the .data 235 | section. As a consequence the location of variables were not accurate. 236 | 237 | 238 | ### References 239 | 1. https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries 240 | - This is probably the holy grail of pic. 241 | 2. http://infocenter.arm.com/help/topic/com.arm.doc.dai0242a/DAI0242A_dynamic_linking_with_rvct.pdf 242 | - Explains what dynamic linking is and some details on how to implement it 243 | 3. https://static.docs.arm.com/ihi0044/f/IHI0044F_aaelf.pdf 244 | - ARM ELF specification. This document contains all the details about the ARM elf like relocation types, etc. 245 | 4. https://www.airs.com/blog/archives/38 246 | - 20 part series on how linkers work 247 | 5. https://stackoverflow.com/a/50701832 248 | - Minimal example of creating GOT. Does not have PLT. 249 | 6. http://www.chibios.com/forum/viewtopic.php?f=3&t=1229 250 | - Explains different ways to achive dynamic module loading 251 | 7. https://interrupt.memfault.com/blog/cortex-m-fault-debug 252 | - Information on debugging faults 253 | 8. https://community.arm.com/developer/ip-products/processors/f/cortex-m-forum/45919/gcc-does-not-generate-correct-code-while-building-pic 254 | 9. https://answers.launchpad.net/gcc-arm-embedded/+question/689355 255 | 10. https://stackoverflow.com/questions/50655162/stm32-position-independent-binaries/50701832#50701832 256 | 11. https://answers.launchpad.net/gcc-arm-embedded/+question/669758 257 | 12 https://answers.launchpad.net/gcc-arm-embedded/+question/675869 258 | --------------------------------------------------------------------------------