PA1 -- Implement infrastructure, expression evaluation, and monitoring points

Posted by Clintonio on Sun, 12 May 2019 05:24:04 +0200

PA1 Summary

Infrastructure

What is needed before the basic implementation is to read the source code and simulate the registers.Experiments have given you hints as to what anonymity is if you need to simulate registers.Register emulation is only possible after you know what union is, with the implementation code attached (code is never unique).

 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;

After the register emulation is implemented, the infrastructure is built.

PA1.1 Infrastructure

Step execution

There are two key points to achieving the function of one-step execution:

  1. Learn the strtok() function.Find the official document for the library function directly.
  2. Call the cpu_exec() function.
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;
};


Note: The code comment section attached is for testing purposes.

Print Register

The print register part is easier to implement, just use the strtok() function to split the string into the desired part.

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();    //This part is used later to print the status of the monitoring point, which can be commented out earlier.
    }
     
    return 0;
}  

###Scan Memory
This section gives the code directly, without any special logic for thinking.

static int cmd_x(char *args){
    //Get the memory start address and scan length.
    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);
    //Scan memory for four bytes at a time;
    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.2 expression evaluation
This part took me a long time, and was supplemented by the following experiments.For this part, I will not post all the source code, only the experimental results and ideas.
The whole expression evaluation process is divided into two parts:

1. Lexical analysis (token recognition)

When doing this analysis, the first step is to understand the regular expression.Regular expressions are useful, amazing, and most importantly complex.Here I would like to talk about the pit I have traveled through in realizing this part.
The first pit is undoubtedly the writing of regular expressions. What I want to say about this is: test as much as you can, and try to be as comprehensive as possible.Only then can a regular expression be written that can be used.
What I need to do after tokens recognition is to store the token information in the tokens [] array, where I'm using the strncpy() function, which better controls where string replication begins.
A simple lexical analyzer has been implemented.

2. Recursive evaluation

Now that you have identified and recorded all the token information, you can perform an evaluation, and you have a complete recursive framework for the entire evaluation, and I will go into more detail about two of these function functions.

  1. Bracket Matching Function
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 if there is no () at the beginning or end 
    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) returns false
    }                              
    if( tag != 0 ) return false;   
    return true;                   
} 
  1. Main Operator Find Function
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;
}              

Where pir is the priority function, here I give priority to different operators according to the c language standard.
After completing the above functions, you have already implemented the basic expression evaluation function, followed by an extension of the expression evaluation - - the identification of negative numbers and pointers.

 for(int i = 0 ;i<nr_token;i++) {
    if(tokens[i].type=='*'&&(i==0||(tokens[i-1].type!=TK_NUM10&&tokens[i-1].type!=TK_LEFT&&tokens[i].type!=TK_NUM16)))
        tokens[i].type = TK_POINT;
    if(tokens[i].type=='-'&&(i==0||(tokens[i-1].type!=TK_NUM10&&tokens[i-1].type!=TK_LEFT&&tokens[i].type!=TK_NUM16)))
        tokens[i].type = TK_NEG;
  }  

When recognizing negative numbers and pointers, it is important to note the special cases of multiple negative signs and pointers. Here I add some code when processing:

       if(tokens[op].type == TK_NEG){
            for( i = op ; i<nr_token ; i++){
                if(tokens[i].type == TK_NUM10){
                    sscanf(tokens[i].str, "%x", &result);
                    //printf("%d \n",result);
                    // return -result;
                    break;
                }
               
            }  
            for( ;i > 0 ;i --) result = -result;
            return result;
        }      
        else if (tokens[op].type == TK_POINT){
            for( i = op ; i<nr_token ; i++){
                if(tokens[i].type == TK_NUM10){
                    sscanf(tokens[op+1].str, "%x", &result);
                   // result = vaddr_read(result, 4);
                   // return result;
                    break;
                }
            }  
            for( ;i > 0 ;i -- ) vaddr_read(result, 4);
            return result;
               
        } 

The logic here is to count the number of symbols and then perform the corresponding number of operations.
This completes PA1.2.

PA1.3 Implement Watch Points

There are three main functions for the implementation of the monitoring point function (you can think for yourself about the type of structure required by the monitoring point, and you can achieve the function):

  1. Add monitoring points:
//You need to store expressions and results.
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;
} 


Before adding any more monitoring points, I made a judgment that the primary purpose of this judgment is to determine whether the monitoring point pool has finished initializing.
3. Release monitoring points

void free_wp(int no){
   WP *p = head;
   if(head == NULL){                                                                                                                                          
       printf("The list of monitoring points is empty. \n");
       assert(0);
   }
   else if(p->NO == no){
       head = head->next;
       p->value = 0;
       p->isused = false; 
       p->next = free_;
       free_ = p;
       printf("Has been deleted%d Monitoring points.\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("Has been deleted%d Monitoring points.\n", no);
        //       free(p);free(q);
               return;
           }
           else{
               p = p -> next;
               q = q -> next;
       }
   }
   printf("No Part%d Monitoring points.\n",no);
   return;
}
  1. Print Watch Point
void print_wp(){
    WP *p = head;
    if(p ==NULL){
        printf("Watch point is empty!\n");
        return;
    }
    else{
        while(p!=NULL){
  
            printf("%d   %s 0x%08x\n",p->NO , p->expr, p->value);
            p=p->next;
        }
        return;
    }
    return;
} 


Now that the entire PA1 experiment has been completed, everyone will have their own answers to the questions they are thinking about.

Topics: C