1) biner yang dikompilasi ditulis ke prom / flash ya. USB, serial, i2c, jtag, dll tergantung pada perangkat apa yang didukung oleh perangkat itu, tidak dapat dipahami untuk memahami proses booting.
2) Ini biasanya tidak benar untuk mikrokontroler, kasus penggunaan utama adalah memiliki instruksi dalam rom / flash dan data dalam ram. Tidak masalah apa arsitekturnya. untuk non-mikrokontroler, pc Anda, laptop Anda, server Anda, program ini disalin dari non-volatile (disk) ke ram kemudian dijalankan dari sana. Beberapa mikrokontroler membiarkan Anda menggunakan ram juga, bahkan yang mengklaim harvard meskipun tampaknya melanggar definisi. Tidak ada apa-apa tentang harvard yang mencegah Anda dari memetakan ram ke sisi instruksi, Anda hanya perlu memiliki mekanisme untuk mendapatkan instruksi di sana setelah daya menyala (yang melanggar definisi, tetapi sistem harvard harus melakukan itu untuk menjadi berguna lainnya selain sebagai mikrokontroler).
3) semacam.
Setiap cpu "boot" dengan cara deterministik, seperti yang dirancang. Cara yang paling umum adalah tabel vektor di mana alamat untuk instruksi pertama yang dijalankan setelah powering berada dalam vektor reset, alamat yang dibaca perangkat keras kemudian menggunakan alamat itu untuk mulai berjalan. Cara umum lainnya adalah membuat prosesor mulai mengeksekusi tanpa tabel vektor di beberapa alamat yang terkenal. Kadang-kadang chip akan memiliki "tali", beberapa pin yang dapat Anda ikat tinggi atau rendah sebelum melepaskan reset, yang digunakan logika untuk mem-boot cara yang berbeda. Anda harus memisahkan cpu itu sendiri, inti prosesor dari sisa sistem. Memahami bagaimana cpu beroperasi, dan kemudian mengerti bahwa perancang chip / sistem memiliki decoder alamat penyiapan di luar cpu sehingga beberapa bagian dari ruang alamat cpus berkomunikasi dengan flash, dan beberapa dengan ram dan beberapa dengan periferal (uart, i2c, spi, gpio, dll). Anda dapat mengambil cpu core yang sama jika diinginkan, dan membungkusnya secara berbeda. Ini adalah apa yang Anda dapatkan ketika Anda membeli sesuatu berdasarkan lengan atau mips. lengan dan pel membuat core cpu, yang chip orang membeli dan membungkus barang-barang mereka sendiri, untuk berbagai alasan mereka tidak membuat barang-barang itu kompatibel dari merek ke merek. Itulah sebabnya jarang bisa mengajukan pertanyaan lengan generik ketika datang ke apa pun di luar inti.
Sebuah mikrokontroler berusaha menjadi sistem pada sebuah chip, sehingga memori non-volatile (flash / rom), volatile (sram), dan cpu semuanya berada pada chip yang sama bersama dengan campuran periferal. Tetapi chip dirancang secara internal sehingga flash dipetakan ke dalam ruang alamat cpu yang sesuai dengan karakteristik boot cpu itu. Jika misalnya cpu memiliki reset vektor di alamat 0xFFFC, maka perlu ada flash / rom yang menanggapi alamat yang dapat kita program melalui 1), bersama dengan cukup flash / rom di ruang alamat untuk program yang bermanfaat. Perancang chip dapat memilih untuk memiliki 0x1000 byte flash mulai dari 0xF000 untuk memenuhi persyaratan tersebut. Dan mungkin mereka menaruh sejumlah ram di alamat yang lebih rendah atau mungkin 0x0000, dan periferal di suatu tempat di tengah.
Arsitektur cpu lain mungkin mulai dieksekusi pada alamat nol, jadi mereka perlu melakukan yang sebaliknya, letakkan flash sehingga ia menjawab kisaran alamat sekitar nol. katakanlah 0x0000 hingga 0x0FFF misalnya. dan kemudian menaruh beberapa ram di tempat lain.
Desainer chip tahu bagaimana cpu melakukan booting dan mereka telah menempatkan penyimpanan non-volatil di sana (flash / rom). Terserah orang perangkat lunak untuk menulis kode boot agar sesuai dengan perilaku cpu yang terkenal itu. Anda harus meletakkan alamat penyetelan ulang vektor dalam penyetelan ulang dan kode booting Anda pada alamat yang Anda tentukan dalam penyetelan ulang vektor. Toolchain dapat sangat membantu Anda di sini. kadang-kadang, terutama dengan point dan klik ides atau kotak pasir lain yang dapat mereka lakukan sebagian besar untuk Anda semua yang Anda lakukan adalah memanggil apis dalam bahasa tingkat tinggi (C).
Namun, bagaimanapun hal itu dilakukan, program yang dimuat ke dalam flash / rom harus sesuai dengan perilaku boot bawaan cpu. Sebelum bagian C dari main program Anda () dan aktif jika Anda menggunakan main sebagai titik masuk Anda, beberapa hal harus dilakukan. Pemrogram AC mengasumsikan bahwa ketika mendeklarasikan variabel dengan nilai awal, mereka berharap itu benar-benar berfungsi. Nah, variabel, selain yang const, berada di ram, tetapi jika Anda memiliki satu dengan nilai awal bahwa nilai awal harus dalam ram non-volatile. Jadi ini adalah segmen .data dan bootstrap C perlu menyalin hal .data dari flash ke ram (di mana biasanya ditentukan untuk Anda oleh toolchain). Variabel global yang Anda nyatakan tanpa nilai awal diasumsikan nol sebelum program Anda dimulai meskipun Anda seharusnya tidak benar-benar berasumsi itu dan untungnya beberapa kompiler mulai memperingatkan tentang variabel yang tidak diinisialisasi. Ini adalah segmen .bss, dan nol bootstrap C yang keluar di ram, konten, nol, tidak harus disimpan dalam memori non-volatil, tetapi alamat awal dan berapa banyak. Sekali lagi toolchain sangat membantu Anda di sini. Dan terakhir minimum adalah Anda perlu mengatur stack pointer karena program C berharap dapat memiliki variabel lokal dan memanggil fungsi lainnya. Maka mungkin beberapa hal spesifik chip lainnya dilakukan, atau kami membiarkan sisanya terjadi pada C. tidak harus disimpan dalam memori non-volatile, tetapi alamat awal dan berapa banyak. Sekali lagi toolchain sangat membantu Anda di sini. Dan terakhir minimum adalah Anda perlu mengatur stack pointer karena program C berharap dapat memiliki variabel lokal dan memanggil fungsi lainnya. Maka mungkin beberapa hal spesifik chip lainnya dilakukan, atau kami membiarkan sisanya terjadi pada C. tidak harus disimpan dalam memori non-volatile, tetapi alamat awal dan berapa banyak. Sekali lagi toolchain sangat membantu Anda di sini. Dan terakhir minimum adalah Anda perlu mengatur stack pointer karena program C berharap dapat memiliki variabel lokal dan memanggil fungsi lainnya. Maka mungkin beberapa hal spesifik chip lainnya dilakukan, atau kami membiarkan sisanya terjadi pada C.
Core seri korteks-m dari arm akan melakukan beberapa hal ini untuk Anda, stack pointer ada di tabel vektor, ada vektor reset untuk menunjuk kode yang akan dijalankan setelah reset, sehingga selain apa pun yang harus Anda lakukan untuk menghasilkan tabel vektor (yang biasanya Anda gunakan asm), Anda dapat menggunakan C murni tanpa asm. sekarang Anda tidak menyalin data Anda. Atau .bss Anda memusatkan perhatian sehingga Anda harus melakukannya sendiri jika Anda ingin mencoba untuk pergi tanpa asm pada sesuatu berbasis korteks-m. Fitur yang lebih besar bukanlah vektor reset tetapi interupsi vektor di mana perangkat keras mengikuti lengan yang direkomendasikan konvensi pemanggilan C dan menyimpan register untuk Anda, dan menggunakan pengembalian yang benar untuk vektor itu, sehingga Anda tidak perlu membungkus asm yang tepat di sekitar masing-masing handler ( atau memiliki arahan spesifik toolchain untuk target Anda agar toolchain membungkusnya untuk Anda).
Hal-hal spesifik chip mungkin misalnya, mikrokontroler sering digunakan dalam sistem berbasis baterai, sehingga daya rendah sehingga beberapa keluar dari reset dengan sebagian besar periferal dimatikan, dan Anda harus mengaktifkan masing-masing sub sistem ini sehingga Anda dapat menggunakannya . Uarts, gpios, dll. Seringkali kecepatan clock ish rendah digunakan, langsung dari kristal atau osilator internal. Dan desain sistem Anda mungkin menunjukkan bahwa Anda membutuhkan jam yang lebih cepat, jadi Anda menginisialisasi itu. jam Anda mungkin terlalu cepat untuk flash atau ram sehingga Anda mungkin perlu mengubah status tunggu sebelum menaikkan jam. Mungkin perlu mengatur uart, atau usb atau antarmuka lainnya. maka aplikasi Anda dapat melakukan hal tersebut.
Desktop komputer, laptop, server, dan mikrokontroler tidak berbeda dalam cara mereka boot / bekerja. Kecuali bahwa mereka tidak sebagian besar dalam satu chip. Program bios sering pada chip flash / rom terpisah dari cpu. Meskipun baru-baru ini CPU x86 menarik lebih banyak dari apa yang dulunya merupakan chip pendukung ke dalam paket yang sama (pengontrol pcie, dll.) Tetapi Anda masih memiliki sebagian besar ram dan rom dari chip Anda, tetapi masih berupa sistem dan masih berfungsi dengan tepat sama di level tinggi. Proses booting cpu sudah dikenal luas, para desainer papan menempatkan flash / rom di ruang alamat di mana cpu boot. program itu (bagian dari BIOS pada PC x86) melakukan semua hal yang disebutkan di atas, ia memulai berbagai peripheral, menginisialisasi dram, menghitung bus pcie, dan sebagainya. Seringkali cukup dapat dikonfigurasi oleh pengguna berdasarkan pada pengaturan bios atau apa yang kita sebut pengaturan cmos, karena pada saat itulah teknologi digunakan. Tidak masalah, ada pengaturan pengguna yang dapat Anda buka dan ubah untuk memberi tahu kode booting BIOS bagaimana memvariasikan apa yang dilakukannya.
orang yang berbeda akan menggunakan terminologi yang berbeda. sebuah chip boot, itu adalah kode pertama yang berjalan. kadang-kadang disebut bootstrap. bootloader dengan kata loader sering berarti bahwa jika Anda tidak melakukan apa pun untuk mengganggu itu adalah bootstrap yang membawa Anda dari booting generik ke sesuatu yang lebih besar, aplikasi atau sistem operasi Anda. tetapi bagian loader menyiratkan bahwa Anda dapat mengganggu proses boot dan kemudian mungkin memuat program pengujian lainnya. jika Anda pernah menggunakan uboot misalnya pada sistem linux tertanam, Anda dapat menekan tombol dan menghentikan boot normal maka Anda dapat mengunduh kernel tes ke ram dan boot itu bukan yang ada di flash, atau Anda dapat mengunduh program sendiri, atau Anda dapat mengunduh kernel baru kemudian minta bootloader menuliskannya ke flash sehingga lain kali Anda mem-boot-nya menjalankan hal-hal baru.
Sejauh CPU itu sendiri, prosesor inti, yang tidak tahu ram dari flash dari periferal. Tidak ada gagasan tentang bootloader, sistem operasi, aplikasi. Itu hanya urutan instruksi yang dimasukkan ke dalam cpu untuk dieksekusi. Ini adalah istilah perangkat lunak untuk membedakan tugas pemrograman yang berbeda satu sama lain. Konsep perangkat lunak satu sama lain.
Beberapa mikrokontroler memiliki bootloader terpisah yang disediakan oleh vendor chip dalam flash terpisah atau area flash terpisah yang mungkin tidak dapat Anda modifikasi. Dalam hal ini sering ada pin atau set pin (saya menyebutnya straps) bahwa jika Anda mengikatnya tinggi atau rendah sebelum reset dilepaskan Anda memberi tahu logika dan / atau bahwa bootloader apa yang harus dilakukan, misalnya satu kombinasi tali mungkin beri tahu chip untuk menjalankan bootloader itu dan tunggu di uart untuk data diprogram ke dalam flash. Atur pengikat dengan cara lain dan program Anda melakukan booting bukan bootloader vendor chip, memungkinkan untuk pemrograman di lapangan chip atau memulihkan dari crash program Anda. Terkadang itu hanya logika murni yang memungkinkan Anda untuk memprogram flash. Ini sangat umum hari ini,
Alasan mengapa sebagian besar mikrokontroler memiliki lebih banyak flash daripada ram adalah bahwa use case utama adalah menjalankan program langsung dari flash, dan hanya memiliki cukup ram untuk menutupi tumpukan dan variabel. Meskipun dalam beberapa kasus Anda dapat menjalankan program dari ram yang harus Anda kompilasi dan simpan dalam flash kemudian salin sebelum menelepon.
EDIT
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
notmain.c
int notmain ( void )
{
unsigned int x=1;
unsigned int y;
y = x + 1;
return(0);
}
flash.ld
MEMORY
{
bob : ORIGIN = 0x00000000, LENGTH = 0x1000
ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > bob
.rodata : { *(.rodata*) } > bob
.bss : { *(.bss*) } > ted
.data : { *(.bss*) } > ted AT > bob
}
Jadi ini adalah contoh untuk korteks-m0, korteks-ms semuanya bekerja sama sejauh contoh ini. Chip tertentu, untuk contoh ini, memiliki aplikasi flash di alamat 0x00000000 di ruang alamat lengan dan ram di 0x20000000.
Cara boot cortex-m adalah kata 32 bit pada alamat 0x0000 adalah alamat untuk menginisialisasi stack pointer. Saya tidak perlu banyak tumpukan untuk contoh ini sehingga 0x20001000 akan cukup, jelas harus ada ram di bawah alamat itu (cara lengan mendorong, apakah mengurangi terlebih dahulu kemudian mendorong jadi jika Anda menetapkan 0x20001000 item pertama pada tumpukan adalah di alamat 0x2000FFFC Anda tidak perlu menggunakan 0x2000FFFC). Kata 32 bit pada alamat 0x0004 adalah alamat untuk reset handler, pada dasarnya kode pertama yang berjalan setelah reset. Lalu ada lebih banyak interrupt dan event handler yang spesifik untuk inti dan chip korteks, mungkin sebanyak 128 atau 256, jika Anda tidak menggunakannya maka Anda tidak perlu mengatur tabel untuk mereka, saya memberikan beberapa untuk demonstrasi tujuan.
Saya tidak perlu berurusan dengan. Data atau .bss dalam contoh ini karena saya tahu sudah tidak ada di segmen tersebut dengan melihat kode. Jika ada saya akan menghadapinya, dan akan dalam sedetik.
Jadi tumpukan sudah diatur, periksa, .data diurus, periksa, .bss, periksa, sehingga hal-hal bootstrap C selesai, dapat bercabang ke fungsi entri untuk C. Karena beberapa kompiler akan menambahkan sampah tambahan jika mereka melihat fungsinya main () dan dalam perjalanan ke main, saya tidak menggunakan nama yang tepat, saya menggunakan notmain () di sini sebagai titik masuk C saya. Jadi reset handler memanggil notmain () lalu jika / ketika notmain () mengembalikannya untuk hang yang hanya merupakan infinite loop, mungkin namanya buruk.
Saya sangat percaya pada penguasaan alat, banyak orang tidak, tetapi apa yang akan Anda temukan adalah bahwa setiap pengembang logam telanjang melakukan hal sendiri, karena kebebasan yang hampir sempurna, tidak jauh dari kendala seperti Anda akan membuat aplikasi atau halaman web . Mereka lagi melakukan hal mereka sendiri. Saya lebih suka memiliki kode bootstrap dan skrip tautan saya sendiri. Yang lain bergantung pada rantai alat, atau bermain di kotak pasir vendor tempat sebagian besar pekerjaan dilakukan oleh orang lain (dan jika ada sesuatu yang pecah, Anda berada dalam dunia yang terluka, dan dengan benda-benda logam sering pecah dan dengan cara dramatis).
Jadi merakit, mengkompilasi dan menghubungkan dengan alat gnu saya dapatkan:
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f802 bl 1c <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <notmain>:
1c: 2000 movs r0, #0
1e: 4770 bx lr
Jadi bagaimana bootloader tahu di mana barang-barang berada. Karena kompiler melakukan pekerjaan. Dalam kasus pertama assembler menghasilkan kode untuk flash.s, dan dengan melakukan itu tahu di mana label berada (label hanya alamat seperti nama fungsi atau nama variabel, dll) jadi saya tidak perlu menghitung byte dan mengisi vektor tabel secara manual, saya menggunakan nama label dan assembler melakukannya untuk saya. Sekarang Anda bertanya, jika reset adalah alamat 0x14 mengapa assembler memasukkan 0x15 dalam tabel vektor. Nah ini adalah korteks-m dan boot dan hanya berjalan dalam mode ibu jari. Dengan ARM saat Anda bercabang ke alamat jika mode percabangan ke ibu jari lsbit perlu diatur, jika mode lengan kemudian diatur ulang. Jadi Anda selalu membutuhkan set bit itu. Saya tahu alat dan dengan menempatkan. Thumb_func di depan label, jika label itu digunakan seperti dalam tabel vektor atau untuk bercabang ke atau apa pun. Toolchain tahu untuk mengatur lsbit. Jadi ada di sini 0x14 | 1 = 0x15. Begitu juga untuk hang. Sekarang disassembler tidak menunjukkan 0x1D untuk panggilan ke notmain () tapi jangan khawatir alat-alat telah membangun instruksi dengan benar.
Sekarang kode di notmain, variabel-variabel lokal, tidak digunakan, mereka adalah kode mati. Kompilator bahkan mengomentari fakta itu dengan mengatakan y diatur tetapi tidak digunakan.
Perhatikan ruang alamat, semuanya dimulai dari alamat 0x0000 dan pergi dari sana sehingga tabel vektor ditempatkan dengan benar, ruang teks atau program juga ditempatkan dengan benar, bagaimana saya mendapat flash.s di depan kode notmain.c adalah dengan Mengetahui alat, kesalahan umum adalah tidak mendapatkan yang benar dan crash dan membakar keras. IMO Anda harus membongkar untuk memastikan hal-hal ditempatkan tepat sebelum Anda boot pertama kali, setelah Anda memiliki barang-barang di tempat yang tepat Anda tidak perlu memeriksa setiap waktu. Hanya untuk proyek baru atau jika mereka hang.
Sekarang sesuatu yang mengejutkan beberapa orang adalah bahwa tidak ada alasan apa pun untuk mengharapkan dua kompiler untuk menghasilkan output yang sama dari input yang sama. Atau bahkan kompiler yang sama dengan pengaturan berbeda. Menggunakan dentang, kompiler llvm saya mendapatkan dua output ini dengan dan tanpa optimasi
llvm / dentang dioptimalkan
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f802 bl 1c <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <notmain>:
1c: 2000 movs r0, #0
1e: 4770 bx lr
tidak dioptimalkan
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f802 bl 1c <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <notmain>:
1c: b082 sub sp, #8
1e: 2001 movs r0, #1
20: 9001 str r0, [sp, #4]
22: 2002 movs r0, #2
24: 9000 str r0, [sp, #0]
26: 2000 movs r0, #0
28: b002 add sp, #8
2a: 4770 bx lr
Jadi itu adalah kebohongan kompilator tidak mengoptimalkan penambahan, tapi itu mengalokasikan dua item pada stack untuk variabel, karena ini adalah variabel lokal mereka di ram tetapi pada stack tidak pada alamat tetap, akan melihat dengan global bahwa perubahan. Tetapi kompiler menyadari bahwa ia dapat menghitung y pada waktu kompilasi dan tidak ada alasan untuk menghitungnya pada saat dijalankan sehingga ia hanya menempatkan 1 pada ruang stack yang dialokasikan untuk x dan 2 untuk ruang stack yang dialokasikan untuk y. kompiler "mengalokasikan" ruang ini dengan tabel internal saya mendeklarasikan stack plus 0 untuk variabel y dan stack plus 4 untuk variabel x. kompiler dapat melakukan apa saja yang diinginkan selama kode yang diterapkan sesuai dengan standar C atau ekspetasi programmer C. Tidak ada alasan mengapa kompiler harus meninggalkan x pada stack + 4 selama durasi fungsi,
Jika saya menambahkan fungsi dummy di assembler
.thumb_func
.globl dummy
dummy:
bx lr
dan kemudian menyebutnya
void dummy ( unsigned int );
int notmain ( void )
{
unsigned int x=1;
unsigned int y;
y = x + 1;
dummy(y);
return(0);
}
output berubah
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f804 bl 20 <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <dummy>:
1c: 4770 bx lr
...
00000020 <notmain>:
20: b510 push {r4, lr}
22: 2002 movs r0, #2
24: f7ff fffa bl 1c <dummy>
28: 2000 movs r0, #0
2a: bc10 pop {r4}
2c: bc02 pop {r1}
2e: 4708 bx r1
sekarang kita memiliki fungsi bersarang, fungsi notmain perlu mempertahankan alamat pengirimnya, sehingga ia dapat mengalahkan alamat pengirim untuk panggilan bersarang. ini karena lengan menggunakan register untuk pengembalian, jika ia menggunakan stack seperti katakanlah x86 atau yang lainnya dengan baik ... ia masih menggunakan stack tetapi berbeda. Sekarang Anda bertanya mengapa itu mendorong r4? Nah, konvensi pemanggilan belum lama berubah untuk menjaga tumpukan disejajarkan pada batas 64 bit (dua kata) alih-alih 32 bit, batas satu kata. Jadi mereka perlu mendorong sesuatu agar tumpukan tetap selaras, sehingga kompiler secara sewenang-wenang memilih r4 karena suatu alasan, tidak masalah mengapa. Popping ke r4 akan menjadi bug meskipun sesuai dengan konvensi pemanggilan untuk target ini, kita tidak clobber r4 pada panggilan fungsi, kita bisa clobber r0 melalui r3. r0 adalah nilai kembali. Sepertinya itu melakukan optimasi ekor mungkin,
Tapi kita melihat bahwa x dan y matematika dioptimalkan ke nilai hardcoded 2 yang diteruskan ke fungsi dummy (dummy secara khusus dikodekan dalam file terpisah, dalam hal ini asm, sehingga kompiler tidak akan mengoptimalkan fungsi memanggil sepenuhnya, jika saya memiliki fungsi dummy yang hanya dikembalikan dalam C di notmain.c optimizer akan menghapus panggilan fungsi x, y, dan dummy karena semuanya mati / kode tidak berguna).
Juga catat bahwa karena kode flash.s jadi lebih besar bukan yang utama juga ada di sini dan toolchain telah mengurus untuk menambal semua alamat untuk kami sehingga kami tidak perlu melakukannya secara manual.
dentang tidak dioptimalkan untuk referensi
00000020 <notmain>:
20: b580 push {r7, lr}
22: af00 add r7, sp, #0
24: b082 sub sp, #8
26: 2001 movs r0, #1
28: 9001 str r0, [sp, #4]
2a: 2002 movs r0, #2
2c: 9000 str r0, [sp, #0]
2e: f7ff fff5 bl 1c <dummy>
32: 2000 movs r0, #0
34: b002 add sp, #8
36: bd80 pop {r7, pc}
dentang dioptimalkan
00000020 <notmain>:
20: b580 push {r7, lr}
22: af00 add r7, sp, #0
24: 2002 movs r0, #2
26: f7ff fff9 bl 1c <dummy>
2a: 2000 movs r0, #0
2c: bd80 pop {r7, pc}
penulis kompiler yang memilih untuk menggunakan r7 sebagai variabel dummy untuk menyelaraskan tumpukan, juga itu membuat frame pointer menggunakan r7 meskipun ia tidak memiliki apa pun dalam bingkai tumpukan. pada dasarnya instruksi bisa dioptimalkan. tetapi menggunakan pop untuk mengembalikan bukan tiga instruksi, itu mungkin pada saya, saya yakin saya bisa mendapatkan gcc untuk melakukannya dengan opsi baris perintah yang tepat (menentukan prosesor).
ini sebagian besar harus menjawab sisa pertanyaan Anda
void dummy ( unsigned int );
unsigned int x=1;
unsigned int y;
int notmain ( void )
{
y = x + 1;
dummy(y);
return(0);
}
Saya memiliki global sekarang. jadi mereka masuk baik data atau .bss jika mereka tidak dioptimalkan.
sebelum kita melihat hasil akhirnya mari kita lihat objek itermediate
00000000 <notmain>:
0: b510 push {r4, lr}
2: 4b05 ldr r3, [pc, #20] ; (18 <notmain+0x18>)
4: 6818 ldr r0, [r3, #0]
6: 4b05 ldr r3, [pc, #20] ; (1c <notmain+0x1c>)
8: 3001 adds r0, #1
a: 6018 str r0, [r3, #0]
c: f7ff fffe bl 0 <dummy>
10: 2000 movs r0, #0
12: bc10 pop {r4}
14: bc02 pop {r1}
16: 4708 bx r1
...
Disassembly of section .data:
00000000 <x>:
0: 00000001 andeq r0, r0, r1
sekarang ada info yang hilang dari ini tetapi memberikan gambaran tentang apa yang terjadi, penghubung adalah orang yang mengambil objek dan menautkannya bersama dengan informasi yang disediakan (dalam hal ini flash.ld) yang memberitahukan di mana .text dan. data dan semacamnya. kompiler tidak tahu hal-hal seperti itu, ia hanya dapat fokus pada kode yang disajikan, setiap eksternal harus meninggalkan lubang bagi linker untuk mengisi koneksi. Data apa pun harus meninggalkan cara untuk menghubungkan hal-hal itu bersama-sama, sehingga alamat untuk semuanya adalah nol di sini hanya karena kompiler dan pembongkar ini tidak tahu. ada info lain yang tidak ditampilkan di sini yang digunakan tautan untuk menempatkan sesuatu. kode di sini cukup independen sehingga penghubung dapat melakukan tugasnya.
kita kemudian melihat setidaknya pembongkaran dari output terkait
00000020 <notmain>:
20: b510 push {r4, lr}
22: 4b05 ldr r3, [pc, #20] ; (38 <notmain+0x18>)
24: 6818 ldr r0, [r3, #0]
26: 4b05 ldr r3, [pc, #20] ; (3c <notmain+0x1c>)
28: 3001 adds r0, #1
2a: 6018 str r0, [r3, #0]
2c: f7ff fff6 bl 1c <dummy>
30: 2000 movs r0, #0
32: bc10 pop {r4}
34: bc02 pop {r1}
36: 4708 bx r1
38: 20000004 andcs r0, r0, r4
3c: 20000000 andcs r0, r0, r0
Disassembly of section .bss:
20000000 <y>:
20000000: 00000000 andeq r0, r0, r0
Disassembly of section .data:
20000004 <x>:
20000004: 00000001 andeq r0, r0, r1
kompiler pada dasarnya meminta dua variabel 32 bit dalam ram. Salah satunya dalam .bss karena saya tidak menginisialisasi sehingga diasumsikan init sebagai nol. yang lain adalah data. Karena saya menginisialisasi pada deklarasi.
Sekarang karena ini adalah variabel global, diasumsikan bahwa fungsi lain dapat memodifikasinya. kompiler tidak membuat asumsi kapan notmain dapat dipanggil sehingga ia tidak dapat mengoptimalkan dengan apa yang dapat dilihatnya, y = x + 1 matematika, sehingga harus melakukan runtime itu. Itu harus membaca dari ram kedua variabel menambahkannya dan menyimpan kembali.
Sekarang jelas kode ini tidak akan berfungsi. Mengapa? karena bootstrap saya seperti yang ditunjukkan di sini tidak menyiapkan ram sebelum memanggil notmain, jadi sampah apa pun yang ada di 0x20000000 dan 0x20000004 ketika chip bangun adalah apa yang akan digunakan untuk y dan x.
Tidak akan menunjukkan itu di sini. Anda dapat membaca bertele-tele bahkan lebih lama saya pada. data dan .bss dan mengapa saya tidak pernah membutuhkannya dalam kode logam kosong saya, tetapi jika Anda merasa Anda harus dan ingin menguasai alat daripada berharap orang lain melakukannya dengan benar .. .
https://github.com/dwelch67/raspberrypi/tree/master/bssdata
skrip linker, dan bootstrap agak spesifik untuk kompiler sehingga semua yang Anda pelajari tentang satu versi dari satu kompiler dapat dilemparkan ke versi berikutnya atau dengan beberapa kompiler lain, namun alasan lain mengapa saya tidak melakukan banyak upaya dalam persiapan data dan .bss hanya untuk menjadi malas ini:
unsigned int x=1;
Saya lebih suka melakukan ini
unsigned int x;
...
x = 1;
dan biarkan kompiler memasukkannya ke dalam .text untuk saya. Terkadang menyimpan flash dengan cara itu terkadang membakar lebih banyak. Tentunya jauh lebih mudah untuk memprogram dan port dari versi toolchain atau satu kompiler ke yang lain. Jauh lebih andal, lebih sedikit kesalahan. Yap, tidak sesuai dengan standar C.
sekarang bagaimana jika kita membuat global yang statis ini?
void dummy ( unsigned int );
static unsigned int x=1;
static unsigned int y;
int notmain ( void )
{
y = x + 1;
dummy(y);
return(0);
}
baik
00000020 <notmain>:
20: b510 push {r4, lr}
22: 2002 movs r0, #2
24: f7ff fffa bl 1c <dummy>
28: 2000 movs r0, #0
2a: bc10 pop {r4}
2c: bc02 pop {r1}
2e: 4708 bx r1
jelas variabel-variabel itu tidak dapat dimodifikasi oleh kode lain, sehingga kompiler sekarang dapat pada waktu kompilasi mengoptimalkan kode mati, seperti yang dilakukan sebelumnya.
tidak dioptimalkan
00000020 <notmain>:
20: b580 push {r7, lr}
22: af00 add r7, sp, #0
24: 4804 ldr r0, [pc, #16] ; (38 <notmain+0x18>)
26: 6800 ldr r0, [r0, #0]
28: 1c40 adds r0, r0, #1
2a: 4904 ldr r1, [pc, #16] ; (3c <notmain+0x1c>)
2c: 6008 str r0, [r1, #0]
2e: f7ff fff5 bl 1c <dummy>
32: 2000 movs r0, #0
34: bd80 pop {r7, pc}
36: 46c0 nop ; (mov r8, r8)
38: 20000004 andcs r0, r0, r4
3c: 20000000 andcs r0, r0, r0
kompiler ini yang menggunakan stack untuk penduduk setempat, sekarang menggunakan ram untuk global dan kode ini seperti yang ditulis rusak karena saya tidak menangani. data atau .bss dengan benar.
dan satu hal terakhir yang tidak bisa kita lihat dalam pembongkaran.
:1000000000100020150000001B0000001B00000075
:100010001B00000000F004F8FFE7FEE77047000057
:1000200080B500AF04480068401C04490860FFF731
:10003000F5FF002080BDC046040000200000002025
:08004000E0FFFF7F010000005A
:0400480078563412A0
:00000001FF
Saya mengubah x menjadi pre-init dengan 0x12345678. Skrip linker saya (ini untuk gnu ld) memiliki ini ted at bob thing. yang memberitahu linker saya ingin tempat terakhir berada di ruang alamat ted, tetapi menyimpannya dalam biner di ruang alamat ted dan seseorang akan memindahkannya untuk Anda. Dan kita bisa melihat itu terjadi. ini adalah format intel hex. dan kita bisa melihat 0x12345678
:0400480078563412A0
ada di ruang alamat flash biner.
readelf juga menunjukkan ini
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x010040 0x00000040 0x00000040 0x00008 0x00008 R 0x4
LOAD 0x010000 0x00000000 0x00000000 0x00048 0x00048 R E 0x10000
LOAD 0x020004 0x20000004 0x00000048 0x00004 0x00004 RW 0x10000
LOAD 0x030000 0x20000000 0x20000000 0x00000 0x00004 RW 0x10000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
baris LOAD di mana alamat virtual 0x20000004 dan fisiknya 0x48