PA1--实现基础设施、表达式求值和监视点

PA1实验总结

    • 基础设施
    • PA1.1基础设施
      • 单步执行
      • 打印寄存器
    • PA1.2 表达式求值
      • 1. 词法分析(识别token)
      • 2. 递归求值
    • PA1.3实现监视点

基础设施

在实现基础实施前需要的是进行源码的阅读,并且实现寄存器的模拟。实验中已经给了提示,如果需要实现寄存器的模拟需要掌握什么是匿名。只有在了解了什么是union后才可以实现寄存器的模拟,下附实现的代码(代码永远不唯一)。

 typedef struct {
    union{
    union
    {   
        uint32_t _32;                                                                             
        uint16_t _16;
        uint8_t _8[2];
    } gpr[8];
           
  /* Do NOT change the order of the GPRs' definitions. */
           
  /* In NEMU, rtlreg_t is exactly uint32_t. This makes RTL instructions
 1. in PA2 able to directly access these registers.
   */   
    struct{
        uint32_t eax;
        uint32_t ecx;
        uint32_t edx;
        uint32_t ebx;
        uint32_t esp;
        uint32_t ebp;
        uint32_t esi;
        uint32_t edi;
    };  
    };  
    vaddr_t eip;
           
} CPU_state;

在实现了寄存器模拟后,开始进行基础设施的搭建。

PA1.1基础设施

单步执行

实现单步执行的功能有两个关键点:

  1. 学习strtok()函数。直接去查找库函数的官方文档即可。
  2. 调用cpu_exec()函数。
static int cmd_si(char *args){   
    char *arg = strtok(args," ");
    // printf("%s\n",arg);
    if(arg == NULL){
        printf("too few arguments.\n");
        return 1;
    }
    int num = atoi(arg);
    cpu_exec(num);
    printf("OK");
    return 0;
};

PA1--实现基础设施、表达式求值和监视点_第1张图片
注:附上的代码注释部分是用来测试用的。

打印寄存器

打印寄存器部分比较容易实现,只需要用strtok()函数将字符串分割成想要的部分即可。

static int cmd_info(char *args){
    char *arg = strtok(args," ");
    printf("%s\n",arg);
    //cpu info
    if (strcmp(arg,"r")==0){
        printf("eax is %x\n",cpu.eax);
        printf("ecx is %x\n",cpu.ecx);
        printf("edx is %x\n",cpu.edx);
        printf("ebx is %x\n",cpu.ebx);
        printf("esp is %x\n",cpu.esp);
        printf("ebp is %x\n",cpu.ebp); 
        printf("esi is %x\n",cpu.esi);
        printf("edi is %x\n",cpu.edi);
        printf("---------------------------\n");
    }
    else if(strcmp(arg,"w")==0){
        print_wp();    //此部分是后期用来打印监测点状态使用,前期可以先注释掉。
    }
     
    return 0;
}  

PA1--实现基础设施、表达式求值和监视点_第2张图片### 扫描内存
这部分直接给出代码,没有什么特别的思考逻辑。

static int cmd_x(char *args){
    //获取内存起始地址和扫描长度。
    if(args == NULL){
        printf("too few parameter! \n");
        return 1;
    }
     
    char *arg = strtok(args," ");
    if(arg == NULL){
        printf("too few parameter! \n");
        return 1;
    }
    int  n = atoi(arg);
    char *EXPR = strtok(NULL," ");
    if(EXPR == NULL){                                                                                                                                          
        printf("too few parameter! \n");
        return 1;
    }
    if(strtok(NULL," ")!=NULL){
        printf("too many parameter! \n");
        return 1;
    }
    bool success = true;
    //vaddr_t addr = expr(EXPR , &success);
    if (success!=true){
        printf("ERRO!!\n");
        return 1;
    }
    char *str;
   // vaddr_t addr = atoi(EXPR);
    vaddr_t addr =  strtol( EXPR,&str,16 );
   // printf("%#lX\n",ad);
    //进行内存扫描,每次四个字节;
    for(int i = 0 ; i < n ; i++){
        uint32_t data = vaddr_read(addr + i * 4,4);
        printf("0x%08x  " , addr + i * 4 );
        for(int j =0 ; j < 4 ; j++){
            printf("0x%02x " , data & 0xff);
            data = data >> 8 ;
        }
        printf("\n");
    }
     
    return 0;
}    

PA1--实现基础设施、表达式求值和监视点_第3张图片

PA1.2 表达式求值

这个部分我用了比较长的时间,而且在做后面的实验时又对这个部分进行了补充。对于这部分,我不会贴出全部源代码,只会给出实验结果和思路。
整个表达式求值的过程我分成了两个部分:

1. 词法分析(识别token)

在进行此法分析时,首先要进行的是了解正则表达式。正则表达式的用途很广,而且很神奇,最重要的是还很复杂。在此我大概说一下自己在实现这个部分是走过的坑。
第一个坑毫无疑问是正则表达式的撰写,对于这个我想说的是:多测试,尽可能想的全面。只有这样才能写出一个可以使用的正则表达式。
在完成tokens识别后需要做的是将token信息存储到tokens[]数组中,这里我使用的是strncpy()函数,因为这个函数可以更好的控制字符串复制的起始位置。
至此就已经实现一个简单的词法分析器了。

2. 递归求值

在识别并记录下全部的token信息后,可以进行求值运算了,在整个求值运算中已经有了一个完整的递归框架,这里我会详细说下其中两个功能函数

  1. 括号匹配函数
bool check_parentheses(int p ,int q){
   // printf("--------------\n");  
    int i,tag = 0;
    if(tokens[p].type != TK_LEFT || tokens[q].type != TK_RIGHT) return false; //首尾没有()则为false 
    for(i = p ; i <= q ; i ++){    
        if(tokens[i].type == TK_LEFT) tag++;
        else if(tokens[i].type == TK_RIGHT) tag--;
        if(tag == 0 && i < q) return false ;  //(3+4)*(5+3) 返回false
    }                              
    if( tag != 0 ) return false;   
    return true;                   
} 
  1. 主操作符寻找函数
int dominant_operator(int p , int q){
               
    int i ,dom = p, left_n = 0;
    int pr = -1 ;
    for(i = p ; i <= q ; i++){
        if(tokens[i].type == TK_LEFT){
            left_n += 1;
            i++;
            while(1){
                if(tokens[i].type == TK_LEFT) left_n += 1;
                else if(tokens[i].type == TK_RIGHT) left_n --;
                i++;
                if(left_n == 0)
                    break;
            }  
            if(i > q)break;
        }      
        else if(tokens[i].type == TK_NUM10) continue;
        else if(pir(tokens[i].type ) > pr){
            pr = pir(tokens[i].type);
            dom = i;
        }      
    }          
   // printf("%d\n",left_n);
    return dom;
}              

其中pir是优先级函数,这里我按照c语言的标准对不同操作符赋予优先级。
在完成以上函数后,就已经实现了基本的表达式求值函数,接下来是对表达式求值的扩展- -负数和指针的识别。

 for(int i = 0 ;i

在进行负数和指针的识别时要注意特殊情况 多个负号和多个指针,这里我在处理时加了一下代码:

       if(tokens[op].type == TK_NEG){
            for( i = op ; i 0 ;i --) result = -result;
            return result;
        }      
        else if (tokens[op].type == TK_POINT){
            for( i = op ; i 0 ;i -- ) vaddr_read(result, 4);
            return result;
               
        } 

这里的逻辑是统计符号的个数,然后进行相应次数的操作。
至此完成PA1.2部分。

PA1.3实现监视点

对于实现监视点功能主要有三个部分函数(监视点所需要的结构类型,大家可以自行思考,能够实现功能即可):

  1. 添加监视点:
//需要存入表达式和结果。
WP *new_wp(char *str , int value){
   if(su == true){
       init_wp_pool();
       su = false;
       //printf("!!!!!!!!!!!!!\n");
   }
   if(free_ == NULL){
       printf("Erro!free is null.\n");
       assert(0);
   }
   WP *new = NULL;
   new = free_;
   free_ = free_->next;
  // printf("!!!!%d\n",value);
  // printf("!!!!%s\n",str);
   new->value = value;
  // printf("!!!!%d\n",new->value);
   strcpy(new->expr, str);
  // printf("!!!!%d\n",new->value);
  // printf("%s /n",new->expr);
   new->next = NULL;
   new->isused = true;
   if(head == NULL) head = new;
   else{
       new->next = head;
       head = new ;
   }
   return new;
} 

PA1--实现基础设施、表达式求值和监视点_第4张图片
再添加监视点前,我进行了一次判断,这个判断主要用途是判定监视点池是否完成初始化。
3. 释放监视点

void free_wp(int no){
   WP *p = head;
   if(head == NULL){                                                                                                                                          
       printf("监视点列表为空。 \n");
       assert(0);
   }
   else if(p->NO == no){
       head = head->next;
       p->value = 0;
       p->isused = false; 
       p->next = free_;
       free_ = p;
       printf("已经删除第%d个监视点。\n", no);
      // free(p);
       return;
   }
   else{
       WP *q = head;
       p = p ->next;
       while(p!=NULL){
           if (p->NO == no){
               q->next = p->next;
               p->value = 0;
               p->isused = false;
               p->next = free_;
               free_ = p;
               printf("已经删除第%d个监视点。\n", no);
        //       free(p);free(q);
               return;
           }
           else{
               p = p -> next;
               q = q -> next;
       }
   }
   printf("不存在第%d个监视点。\n",no);
   return;
}
  1. 打印监视点
void print_wp(){
    WP *p = head;
    if(p ==NULL){
        printf("监视点为空!\n");
        return;
    }
    else{
        while(p!=NULL){
  
            printf("%d   %s 0x%08x\n",p->NO , p->expr, p->value);
            p=p->next;
        }
        return;
    }
    return;
} 

PA1--实现基础设施、表达式求值和监视点_第5张图片
至此已经完成了整个PA1实验,思考题每个人都会有自己的答案。

你可能感兴趣的:(PA实验)