日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

bilibili深入理解计算机系统笔记(1):汇编模拟器能跑了

發(fā)布時間:2025/4/5 windows 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bilibili深入理解计算机系统笔记(1):汇编模拟器能跑了 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 深入理解計算機(jī)系統(tǒng)筆記
    • P1筆記
    • p2筆記
    • p3筆記
    • p4筆記
    • 匯編模擬器(p5-7)
        • 封裝訪存接口
        • 輸出計算機(jī)狀態(tài)
      • 實現(xiàn)指令
        • call指令的實現(xiàn)
    • bug 積累
      • include重復(fù)包含的bug
      • 未定義的類型轉(zhuǎn)換int to uint8_t
      • uint64_t類型printf
    • vs code 快捷鍵記錄
    • makefile
    • gdb調(diào)試積累

深入理解計算機(jī)系統(tǒng)筆記

視頻鏈接:深入理解計算機(jī)系統(tǒng)合集(周更中),作者:yaaangmin

P1筆記

安裝wsl 和在wsl中安裝vscode
[安裝wsl遇到的問題]https://lishizheng.blog.csdn.net/article/details/120549190

[wsl2安裝vscode安裝和配置]https://blog.csdn.net/shizheng_Li/article/details/122342583?spm=1001.2014.3001.5501

p2筆記

命令行編譯c文件格式:

gcc 文件名.c -o 自定義生成的文件名

命令行編譯cpp文件格式:

g++ 文件名.cpp -o 生成的文件名

命令行運(yùn)行cpp文件格式:

./文件名

具體操作如下:

azheng@lishizheng:/mnt/e/csapp_bilibili$ ls a.out main.cpp azheng@lishizheng:/mnt/e/csapp_bilibili$ g++ main.cpp -o main azheng@lishizheng:/mnt/e/csapp_bilibili$ ./main 1 Hello, world azheng@lishizheng:/mnt/e/csapp_bilibili$

上面先編譯后執(zhí)行兩句命令,可以合并為編譯執(zhí)行一起做

g++ main.cpp -o main && ./main

lowbit操作:二進(jìn)制中取出最后位的1,主要用于樹狀數(shù)組(segment array)中

unsigned LowBit(unsigned x) {unsigned a = x & (-x); //等價寫法 unsigned a = x & ((~x) + 1); return a; }

判斷輸入的十六進(jìn)制數(shù)是不是字母(a~f)

對于二進(jìn)制數(shù):a=x3x2x1x0a = x_3x_2x_1x_0a=x3?x2?x1?x0?, 判斷a是十六進(jìn)制中字母(a-f)的邏輯表達(dá)式為: x3&(x1∣x2)x_3 \& (x_1 | x_2)x3?&(x1?x2?)

// 判斷給定的十六進(jìn)制數(shù)是否為a~f這些字母 // return:每四位判斷一下是否為字母,是的話為1,否則為0 // input:0xab, output: 0x11 // input 0xab123abcd, output: 0x110001111 unsigned Letter(unsigned x) {// 每4位取出對應(yīng)的x1,x2,x3// x3 x2 x1 x0 -hex// 0 0 1 0 - hex constant - 0x2// 0 0 x1 0 - result of &// 32 / 4 = 8, we need 8 2s -0x22222222unsigned x1 = x & 0x22222222; // 取出每四位的倒數(shù)第二位x1unsigned x2 = x & 0x44444444; // 取出每四位的倒數(shù)第三位x2unsigned x3 = x & 0x88888888; // 取出每四位的最高位x3//判斷是否為字母的邏輯表達(dá)式: x3 * (x1 + x2) <====> x3 & (x1 + x2)return (x3 >> 3) & ((x1 >> 1) | (x2 >> 2)); // a = 0, a = 1; }

p3筆記

浮點數(shù)(float)類型

符號位、階碼(E)和尾數(shù)(fraction)三部分,共計32位,分別是1 + 8 + 23位。

p4筆記

根據(jù)可執(zhí)行文件,生成匯編代碼的指令(反匯編指令)

objdump -d 文件名 objdump -d main

可輸出如下圖所示的匯編代碼

重定向到某個txt文件,需要使用如下代碼

objdump -d main > main.txt 功能:將main反匯編之后輸出到main.txt文件 code main.txt 功能:用vs code 打開main.txt

補(bǔ)充:vscode隱藏下面輸出面板,快捷鍵是ctrl+jctrl + jctrl+j

進(jìn)程的內(nèi)存空間具體劃分

從下往上依次是:程序數(shù)據(jù)(.data, .bss等),往上依次是堆(malloc),共享庫,用戶棧(user stack),最上面是內(nèi)核地址空間

匯編模擬器(p5-7)

定義reg結(jié)構(gòu)

模仿下圖的寄存器結(jié)構(gòu)定義reg結(jié)構(gòu)體。

reg寄存器舉例

union // union聯(lián)合體,共享低地址內(nèi)存{struct {uint8_t al;uint8_t ah;};uint16_t ax;uint32_t eax;uint64_t rax; };

具體訪存類型的實現(xiàn),九種訪存,兩種其他(立即數(shù),寄存器),通過函數(shù)

static uint64_t decode_od(od_t od)

操作符,操作數(shù),指令的結(jié)構(gòu)體定義

// 操作符的類型 typedef enum OP {MOV, // 0PUSH, // 1CALL, // 2add_reg_reg // 3 } op_t;// 操作數(shù)的類型 typedef struct OD {od_type_t type;int64_t imm;int64_t scal;uint64_t *reg1;uint64_t *reg2;char code[100]; } od_t;// 指令的結(jié)構(gòu)定義 typedef struct INSTRUCT_STRUCT {op_t op; // operatorod_t src; // operandod_t dst; // operand } inst_t;

操作碼譯碼的代碼實現(xiàn)

/*operand decodingimmediate/ register/ memory(9) */ static uint64_t decode_od(od_t od) {if (od.type == IMM){return *(uint64_t *)&od.imm; // &取地址,然后轉(zhuǎn)換為uint64類型的指針,然后再取該地址的值(*)}else if (od.type == REG){// address in registerreturn (uint64_t)od.reg1; }else{// mmuint64_t vaddr = 0;if (od.type == MM_IMM){vaddr = od.imm;}else if (od.type == MM_REG){// store regvaddr = *(od.reg1);}else if (od.type == MM_IMM_REG){vaddr = od.imm + *(od.reg1);}else if (od.type == MM_REG1_REG2){vaddr = *(od.reg1) + *(od.reg2);}else if (od.type == MM_IMM_REG1_REG2){vaddr = od.imm + *(od.reg1) + *(od.reg2);}else if (od.type == MM_RGE2_S){vaddr = (*(od.reg2)) * od.scal;}else if (od.type == MM_IMM_REG2_S){vaddr = (*(od.reg2)) * od.scal + od.imm;}else if (od.type == MM_REG1_REG2_S){vaddr = *(od.reg1) + (*(od.reg2)) * od.scal;}else if (od.type == MM_IMM_REG1_REG2_S){vaddr = *(od.reg1) + (*(od.reg2)) * od.scal + od.imm;}return va2pa(vaddr); // virtual address => physical address}}

指令周期的實現(xiàn)

while (1) {
從PC指示的存儲器位置取出指令;
執(zhí)行指令;
更新PC;
}

代碼

// 指令周期 void instruction_cycle() {// 取指令inst_t *instr = (inst_t *)reg.rip; // instruction address// 譯碼(取源操作數(shù),目的操作數(shù))uint64_t src = decode_od(instr->src);uint64_t dst = decode_od(instr->dst);// 根據(jù)指令的操作類型,查找函數(shù)指針數(shù)組,找到函數(shù)指針// forexample: add rax rbx 這里,op = add_reg_reg = 3handler_t handler = handler_table[instr->op];// 執(zhí)行指令handler(src, dst); }

指令從哪里來?來自PC,這里是rip寄存器

然后pc的值在哪里更新?在具體回調(diào)函數(shù)(handler)里面更新,比如指令mov_reg_reg的handler如下

void mov_reg_reg_handler(uint64_t src, uint64_t dst) {*(uint64_t *)dst = *(uint64_t *)src; // 指令功能reg.rip = reg.rip + sizeof(inst_t); // 更新pc }

簡單的匯編模擬器模擬功能如下圖:只能模擬下圖的15條指令:

(2022年1月9日20點42分更新)匯編指令格式完成15條,具體內(nèi)容為模擬下圖右側(cè)15條匯編指令

結(jié)構(gòu)定義如下

inst_t program[INST_LEN] = {// uint64_t add(uint64_t, uint64_t){push_reg,{ REG, 0, 0, (uint64_t *)&reg.rbp, NULL },{ EMPTY, 0, 0, NULL, NULL },"push \%rbp"},{mov_reg_reg,{ REG, 0, 0, (uint64_t *)&reg.rsp, NULL },{ REG, 0, 0, (uint64_t *)&reg.rbp, NULL },"mov \%rsp,\%rbp"},...// main entry point{mov_reg_reg,{ REG, 0, 0, (uint64_t *)&reg.rdx, NULL },{ REG, 0, 0, (uint64_t *)&reg.rsi, NULL },"mov \%rdx,\%rsi"},{mov_reg_reg,{ REG, 0, 0, (uint64_t *)&reg.rax, NULL },{ REG, 0, 0, (uint64_t *)&reg.rdi, NULL },"mov \%rax,\%rdi"},{call,{ IMM, (uint64_t)&(program[0]), 0, NULL, NULL},{ EMPTY, 0, 0, NULL, NULL},"callq 1129 <add>" // call add函數(shù)的地址},{mov_reg_mem,{ REG, 0, 0, (uint64_t *)&reg.rax, NULL },{ MM_IMM_REG, -0x8, 0, (uint64_t *)&reg.rbp, NULL },"mov \%rax,-0x8(\%rbp)" }, }

完成15條匯編指令的定義之后,我們需要完成對具體指令的實現(xiàn)函數(shù)

使用gdb調(diào)試add函數(shù)

azheng@lishizheng:/mnt/e/csapp_bilibili/ass$ gdb add #使用gdb調(diào)試add函數(shù)(add.c編譯完成的add文件) Type "apropos word" to search for commands related to "word"... Reading symbols from add... (No debugging symbols found in add) (gdb) b main # main函數(shù)加斷點 Breakpoint 1 at 0x114e (gdb) run # 執(zhí)行add函數(shù) Starting program: /mnt/e/csapp_bilibili/ass/addBreakpoint 1, 0x000055555555514e in main () (gdb) disas Dump of assembler code for function main: => 0x000055555555514e <+0>: endbr64 # 正在執(zhí)行的代碼在這一行0x0000555555555152 <+4>: push %rbp0x0000555555555153 <+5>: mov %rsp,%rbp0x0000555555555156 <+8>: sub $0x20,%rsp0x000055555555515a <+12>: movq $0x12340000,-0x18(%rbp)0x0000555555555162 <+20>: movq $0xabcd,-0x10(%rbp)0x000055555555516a <+28>: mov -0x10(%rbp),%rdx0x000055555555516e <+32>: mov -0x18(%rbp),%rax0x0000555555555172 <+36>: mov %rdx,%rsi0x0000555555555175 <+39>: mov %rax,%rdi0x0000555555555178 <+42>: callq 0x555555555129 <add>0x000055555555517d <+47>: mov %rax,-0x8(%rbp)0x0000555555555181 <+51>: mov $0x0,%eax0x0000555555555186 <+56>: leaveq0x0000555555555187 <+57>: retq End of assembler dump. (gdb) ni 8 # 將執(zhí)行的代碼向下跳8行 0x0000555555555172 in main () (gdb) disas # 反匯編 Dump of assembler code for function main:0x000055555555514e <+0>: endbr640x0000555555555152 <+4>: push %rbp0x0000555555555153 <+5>: mov %rsp,%rbp0x0000555555555156 <+8>: sub $0x20,%rsp0x000055555555515a <+12>: movq $0x12340000,-0x18(%rbp)0x0000555555555162 <+20>: movq $0xabcd,-0x10(%rbp)0x000055555555516a <+28>: mov -0x10(%rbp),%rdx0x000055555555516e <+32>: mov -0x18(%rbp),%rax => 0x0000555555555172 <+36>: mov %rdx,%rsi # 正在調(diào)試的代碼在這一行0x0000555555555175 <+39>: mov %rax,%rdi0x0000555555555178 <+42>: callq 0x555555555129 <add>0x000055555555517d <+47>: mov %rax,-0x8(%rbp)0x0000555555555181 <+51>: mov $0x0,%eax0x0000555555555186 <+56>: leaveq0x0000555555555187 <+57>: retq End of assembler dump.

顯示光標(biāo)指向mov %rdi, %rsi 時add函數(shù)后的各個寄存器的值(狀態(tài)機(jī)的一個狀態(tài))

(gdb) info r # 寄存器的值 rax 0x12340000 305397760 rbx 0x555555555180 93824992235904 rcx 0x555555555180 93824992235904 rdx 0xabcd 43981 rsi 0x7fffffffe0e8 140737488347368 rdi 0x1 1 rbp 0x7fffffffdff0 0x7fffffffdff0 rsp 0x7fffffffdfd0 0x7fffffffdfd0 r8 0x0 0 r9 0x7ffff7fe0d50 140737354009936 r10 0x7 7 r11 0x2 2 r12 0x555555555040 93824992235584 r13 0x7fffffffe0e0 140737488347360 r14 0x0 0 r15 0x0 0 rip 0x55555555516a 0x55555555516a <main+36> eflags 0x202 [ IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 (gdb)

(2022年1月10日14點25分更新)

gdb調(diào)試過程中的疑問解釋

由于intel是小端機(jī)器,所以下面的機(jī)器代碼(查看寄存器rsp附近的值)解釋如下:

(gdb) x/10 0x7fffffffdfd0 0x7fffffffdfd0: 0x00000000 0x00000000 0x12340000 0x00000000 0x7fffffffdfe0: 0x0000abcd 0x00000000 0x00000000 0x00000000 0x7fffffffdff0: 0x00000000 0x00000000 (gdb)

首先,每一個0x開頭的是32位二進(jìn)制數(shù),對于64位數(shù)值,兩個0x組成一個數(shù)值,以0x123400000x000000000x12340000 \quad 0x000000000x123400000x00000000兩個0x為例,解釋小端機(jī)器。所謂小端機(jī),就是低地址在前,高地址在后。即0x12340000是低位數(shù),而0x00000000是高位數(shù)。所以這個數(shù)值為

0x0000000012340000 而不是 0x1234000000000000(這是大端機(jī):高地址在前,低地址在后)

兩個寄存器rbp和rsp標(biāo)志著該函數(shù)占用堆棧的起始地址和結(jié)束地址,由于堆棧是向下增長的,所以起始rbp要大于結(jié)束rsp

rbp 0x7fffffffdff0 0x7fffffffdff0 rsp 0x7fffffffdfd0 0x7fffffffdfd0

當(dāng)執(zhí)行完mov %rax,-0x8(%rbp)之后各個寄存器的值(狀態(tài)機(jī)的另一個狀態(tài))

(gdb) ni 4 # 向下執(zhí)行4行 0x0000555555555179 in main () (gdb) disas Dump of assembler code for function main:0x0000555555555146 <+0>: endbr640x000055555555514a <+4>: push %rbp0x000055555555514b <+5>: mov %rsp,%rbp0x000055555555514e <+8>: sub $0x20,%rsp0x0000555555555152 <+12>: movq $0x12340000,-0x18(%rbp)0x000055555555515a <+20>: movq $0xabcd,-0x10(%rbp)0x0000555555555162 <+28>: mov -0x10(%rbp),%rdx0x0000555555555166 <+32>: mov -0x18(%rbp),%rax0x000055555555516a <+36>: mov %rdx,%rsi0x000055555555516d <+39>: mov %rax,%rdi0x0000555555555170 <+42>: callq 0x555555555129 <add>0x0000555555555175 <+47>: mov %rax,-0x8(%rbp) => 0x0000555555555179 <+51>: mov $0x0,%eax0x000055555555517e <+56>: leaveq0x000055555555517f <+57>: retq End of assembler dump. (gdb) info r # 顯示各個寄存器的值 rax 0x1234abcd 305441741 rbx 0x555555555180 93824992235904 rcx 0x555555555180 93824992235904 rdx 0x12340000 305397760 rsi 0xabcd 43981 rdi 0x12340000 305397760 rbp 0x7fffffffdff0 0x7fffffffdff0 rsp 0x7fffffffdfd0 0x7fffffffdfd0 r8 0x0 0 r9 0x7ffff7fe0d50 140737354009936 r10 0x7 7 r11 0x2 2 r12 0x555555555040 93824992235584 r13 0x7fffffffe0e0 140737488347360 r14 0x0 0 r15 0x0 0 rip 0x555555555179 0x555555555179 <main+51> eflags 0x202 [ IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 (gdb)

執(zhí)行完add函數(shù)之后,校驗內(nèi)存(棧頂指針rsp附近的值)

(gdb) x/10 0x7fffffffdfd0 0x7fffffffdfd0: 0x00000000 0x00000000 0x12340000 0x00000000 0x7fffffffdfe0: 0x0000abcd 0x00000000 0x1234abcd 0x00000000 0x7fffffffdff0: 0x00000000 0x00000000 (gdb)add運(yùn)行前內(nèi)存(棧頂指針rsp附近的值) (gdb) x/10 0x7fffffffdfd0 0x7fffffffdfd0: 0x00000000 0x00000000 0x12340000 0x00000000 0x7fffffffdfe0: 0x0000abcd 0x00000000 0x00000000 0x00000000 0x7fffffffdff0: 0x00000000 0x00000000

封裝訪存接口

使用指針訪存是一件很危險的事情,所以需要把對內(nèi)存的操作封裝起來,形成定義好的、安全的接口,統(tǒng)一使用。

同時這里學(xué)習(xí)了flighting 的使用,即定義一個宏,來判斷某項功能是否啟用。這里定義宏SRAM_CACHE_SETTING來判斷cache是否啟用,為后續(xù)程序擴(kuò)展帶來了方便。

// flighting標(biāo)志 #define SRAM_CACHE_SETTING 0 // 是否使用cacheuint64_t read64bits_dram(uint64_t paddr) {if (SRAM_CACHE_SETTING == 1){return 0x0;}uint64_t val = 0x0;val += ((uint64_t)mm[paddr + 0]) << 0;val += ((uint64_t)mm[paddr + 1]) << 8;val += ((uint64_t)mm[paddr + 2]) << 16;val += ((uint64_t)mm[paddr + 3]) << 24;val += ((uint64_t)mm[paddr + 4]) << 32;val += ((uint64_t)mm[paddr + 5]) << 40;val += ((uint64_t)mm[paddr + 6]) << 48;val += ((uint64_t)mm[paddr + 7]) << 56;return val; }void write64bits_dram(uint64_t paddr, uint64_t data) {if (SRAM_CACHE_SETTING == 1){return;}// 將64bits的數(shù)據(jù)拆分,每8bits寫入一個單元// mm[index] 是uint8_t類型mm[paddr + 0] = (data >> 0) & 0xff; // mask 取8位mm[paddr + 1] = (data >> 8) & 0xff; mm[paddr + 2] = (data >> 16) & 0xff; mm[paddr + 3] = (data >> 24) & 0xff; mm[paddr + 4] = (data >> 32) & 0xff; mm[paddr + 5] = (data >> 40) & 0xff; mm[paddr + 6] = (data >> 48) & 0xff; mm[paddr + 7] = (data >> 56) & 0xff; }

輸出計算機(jī)狀態(tài)

兩個函數(shù)print_register和print_stack,用于輸出寄存器和堆棧的值,輔助debug。

// 輸出寄存器狀態(tài) void print_register(); // 輸出堆棧rsp上下各n(=10)個單元的狀態(tài) void print_stack();void print_register() {printf("rax = %16lx\trbx = %16lx\trcx = %16lx\trdx = %16lx\n",reg.rax, reg.rbx, reg.rcx, reg.rdx);printf("rsi = %16lx\trdi = %16lx\trbp = %16lx\trsp = %16lx\n",reg.rsi, reg.rdi, reg.rbp, reg.rsp);printf("rip = %16lx\n", reg.rip); }void print_stack() {int n = 10;uint64_t *high = (uint64_t *)&mm[va2pa(reg.rsp)]; // 棧頂(物理地址) high = &high[n];// 變量rsp_stack:虛擬地址indexinguint64_t rsp_start = reg.rsp + n * 8; // 棧底(高地址):從rsp往上10條指令for (int i = 0; i < 2 * n; i ++) // 從高地址10,到低地址-10位{uint64_t *ptr = (uint64_t *)(high - i);printf("0x%016lx : %16lx", rsp_start, (uint64_t)*ptr);if (i == n){printf(" <== rsp");}rsp_start = rsp_start - 8; // 每次8bytes更新stack的indexingprintf("\n");} }

實現(xiàn)指令

call指令的實現(xiàn)

call指令的實現(xiàn)

主要是兩個寄存器的值rip 和rsp,它們的功能如下:rip存有指令的地址,rsp為棧頂指針.

子程序調(diào)用(call)的具體步驟:

  • call指令執(zhí)行時,堆棧先要向下擴(kuò)展一格:rsp - 8byte。

  • 返回地址(return address):rsp會更新為下一條指令的地址:rsp = rip + sizeof(inst_t);。

  • rip更新為子程序的地址,等待被調(diào)用。

  • 當(dāng)call(調(diào)用子程序)完成后,會根據(jù)返回地址rip跳轉(zhuǎn)到原程序的call指令的下一條指令繼續(xù)執(zhí)行。

代碼實現(xiàn)

// call指令的模擬實現(xiàn) void call_handler(uint64_t src, uint64_t dst) {// src: imm - address of called function// [1] 棧頂先下移一格reg.rsp = reg.rsp - 8; // [2] write return address to rsp memory(返回地址寫入棧頂rsp)write64bits_dram(va2pa(reg.rsp),reg.rip + sizeof(inst_t) //return address: next instruction );// [3] put address of callee to rip,waiting to execute(子程序的地址寫入rip,等待執(zhí)行)reg.rip = src; } // 將data寫入物理內(nèi)存paddr中 /* void write64bits_dram(uint64_t paddr, uint64_t data); */

圖示對上述過程的解釋:

上圖a中rip = 0x400563的值時當(dāng)前指令call的地址,圖b中棧頂值rip = 0x400568這是 return address(執(zhí)行完call調(diào)用的函數(shù)之后,從該地址繼續(xù)執(zhí)行)。從這兩個rip的數(shù)值我們可以看出x86架構(gòu)的call指令占5B(兩個rip的值作差得到)。圖b中rip的值為call的子函數(shù)的入口地址。

bug 積累

include重復(fù)包含的bug

In file included from ./src/disk/code.c:3: ./src/memory/instruction.h:33:5: note: previous definition of ‘MM_IMM_REG1_REG2_S’ was here33 | MM_IMM_REG1_REG2_S| ^~~~~~~~~~~~~~~~~~ In file included from ./src/disk/elf.h:4,from ./src/disk/code.c:4: ./src/memory/instruction.h:37:16: error: redefinition of ‘struct OD’37 | typedef struct OD| ^~ In file included from ./src/disk/code.c:3: ./src/memory/instruction.h:37:16: note: originally defined here37 | typedef struct OD| ^~ In file included from ./src/disk/elf.h:4,from ./src/disk/code.c:4: ./src/memory/instruction.h:44:3: error: conflicting types for ‘od_t’44 | } od_t;

解決辦法是使用include guard,類似于設(shè)計模式里面的單例模式。

//mmu.h // include guard#ifndef mmu_guard // 如果宏mmm_guard 沒有被定義,則往下執(zhí)行,如果宏mmm_guard被定義了,則跳轉(zhuǎn)到#endif#define mmu_guard// memory management unit #include <stdint.h>uint64_t va2pa(uint64_t vaddr);#endif

未定義的類型轉(zhuǎn)換int to uint8_t

azheng@lishizheng:/mnt/e/csapp_bilibili/ass$ make main /usr/bin/gcc-9 -Wall -g -O2 -Werror -std=gnu99 -I./src ./src/memory/instruction.c ./src/cpu/mmu.c ./src/disk/code.c ./src/memory/dram.h ./src/main.c -o program ./src/main.c: In function ‘main’: ./src/main.c:28:33: error: unsigned conversion from ‘int’ to ‘uint8_t’ {aka ‘unsigned char’} changes value from ‘43981’ to ‘205’ [-Werror=overflow]28 | mm[va2pa(0x7fffffffdfe0)] = 0x0000abcd;| ^~~~~~~~~~ ./src/main.c:29:33: error: unsigned conversion from ‘int’ to ‘uint8_t’ {aka ‘unsigned char’} changes value from ‘305397760’ to ‘0’ [-Werror=overflow]29 | mm[va2pa(0x7fffffffdfd8)] = 0x12340000;| ^~~~~~~~~~ cc1: all warnings being treated as errors make: *** [makefile:12: main] Error 1

uint64_t類型printf

azheng@lishizheng:/mnt/e/csapp_bilibili/ass$ make main /usr/bin/gcc-9 -Wall -g -O2 -Werror -std=gnu99 -I./src ./src/memory/instruction.c ./src/cpu/mmu.c ./src/disk/code.c ./src/memory/dram.c ./src/main.c -o program ./src/main.c: In function ‘main’: ./src/main.c:34:16: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘uint64_t’ {aka ‘long unsigned int’} [-Werror=format=]34 | printf("%16x\n",| ~~~^| || unsigned int| %16lx35 | *(uint64_t *)&mm[pa]);| ~~~~~~~~~~~~~~~~~~~~| || uint64_t {aka long unsigned int} cc1: all warnings being treated as errors make: *** [makefile:12: main] Error 1

uint64_t 是long unsigned int,用printf需要控制符%l,以十六進(jìn)制形式輸出%lx

vs code 快捷鍵記錄

復(fù)制某一行: alt + shift + 向下的箭頭

代碼格式化:alt + shift + F

makefile

快速編譯

makefile文件內(nèi)容

CC = /usr/bin/gcc-9 CFLAGS = -Wall -g -O2 -Werror -std=gnu99EXE = programSRC = ./src CODE = ./src/memory/instruction.c ./src/cpu/mmu.c ./src/disk/code.c ./src/memory/dram.c ./src/main.c.PHONY: program main:$(CC) $(CFLAGS) -I$(SRC) $(CODE) -o $(EXE)run:./$(EXE)

gcc編譯選項

圖片來源:https://blog.csdn.net/Ivan804638781/article/details/111996770

編譯main文件

make main

運(yùn)行文件

make run

gdb調(diào)試積累

目前使用gdb調(diào)試的步驟(2022年1月9日更新)

  • gdb調(diào)試add函數(shù)
  • gdb add
  • 對main函數(shù)加斷點
  • b main
  • 運(yùn)行待調(diào)試的函數(shù)
  • run
  • 反匯編main函數(shù)
  • disas
  • 退出gdb
  • quit

    上述序號代表執(zhí)行順序。

    examine(簡寫為x)可以用來查看內(nèi)存地址中的值

    x/[n][f][u] address

    其中:

    • n表示要顯示的內(nèi)存單元個數(shù),默認(rèn)值是1
    • f表示要打印的格式
    • u表示要打印的單元長度
    • address表示內(nèi)存單元地址

    舉例,打印10個內(nèi)存單元

    x/10 address

    總結(jié)

    以上是生活随笔為你收集整理的bilibili深入理解计算机系统笔记(1):汇编模拟器能跑了的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。