Home | History | Annotate | Download | only in minelf
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <errno.h>
     18 #include <sys/stat.h>
     19 #include <fcntl.h>
     20 #include <stdio.h>
     21 #include <unistd.h>
     22 #include <string.h>
     23 #include <strings.h>
     24 #include "Retouch.h"
     25 #include "applypatch/applypatch.h"
     26 
     27 typedef struct {
     28     int32_t mmap_addr;
     29     char tag[4]; /* 'P', 'R', 'E', ' ' */
     30 } prelink_info_t __attribute__((packed));
     31 
     32 #define false 0
     33 #define true 1
     34 
     35 static int32_t offs_prev;
     36 static uint32_t cont_prev;
     37 
     38 static void init_compression_state(void) {
     39     offs_prev = 0;
     40     cont_prev = 0;
     41 }
     42 
     43 // For details on the encoding used for relocation lists, please
     44 // refer to build/tools/retouch/retouch-prepare.c. The intent is to
     45 // save space by removing most of the inherent redundancy.
     46 
     47 static void decode_bytes(uint8_t *encoded_bytes, int encoded_size,
     48                          int32_t *dst_offset, uint32_t *dst_contents) {
     49     if (encoded_size == 2) {
     50         *dst_offset = offs_prev + (((encoded_bytes[0]&0x60)>>5)+1)*4;
     51 
     52         // if the original was negative, we need to 1-pad before applying delta
     53         int32_t tmp = (((encoded_bytes[0] & 0x0000001f) << 8) |
     54                        encoded_bytes[1]);
     55         if (tmp & 0x1000) tmp = 0xffffe000 | tmp;
     56         *dst_contents = cont_prev + tmp;
     57     } else if (encoded_size == 3) {
     58         *dst_offset = offs_prev + (((encoded_bytes[0]&0x30)>>4)+1)*4;
     59 
     60         // if the original was negative, we need to 1-pad before applying delta
     61         int32_t tmp = (((encoded_bytes[0] & 0x0000000f) << 16) |
     62                        (encoded_bytes[1] << 8) |
     63                        encoded_bytes[2]);
     64         if (tmp & 0x80000) tmp = 0xfff00000 | tmp;
     65         *dst_contents = cont_prev + tmp;
     66     } else {
     67         *dst_offset =
     68           (encoded_bytes[0]<<24) |
     69           (encoded_bytes[1]<<16) |
     70           (encoded_bytes[2]<<8) |
     71           encoded_bytes[3];
     72         if (*dst_offset == 0x3fffffff) *dst_offset = -1;
     73         *dst_contents =
     74           (encoded_bytes[4]<<24) |
     75           (encoded_bytes[5]<<16) |
     76           (encoded_bytes[6]<<8) |
     77           encoded_bytes[7];
     78     }
     79 }
     80 
     81 static uint8_t *decode_in_memory(uint8_t *encoded_bytes,
     82                                  int32_t *offset, uint32_t *contents) {
     83     int input_size, charIx;
     84     uint8_t input[8];
     85 
     86     input[0] = *(encoded_bytes++);
     87     if (input[0] & 0x80)
     88         input_size = 2;
     89     else if (input[0] & 0x40)
     90         input_size = 3;
     91     else
     92         input_size = 8;
     93 
     94     // we already read one byte..
     95     charIx = 1;
     96     while (charIx < input_size) {
     97         input[charIx++] = *(encoded_bytes++);
     98     }
     99 
    100     // depends on the decoder state!
    101     decode_bytes(input, input_size, offset, contents);
    102 
    103     offs_prev = *offset;
    104     cont_prev = *contents;
    105 
    106     return encoded_bytes;
    107 }
    108 
    109 int retouch_mask_data(uint8_t *binary_object,
    110                       int32_t binary_size,
    111                       int32_t *desired_offset,
    112                       int32_t *retouch_offset) {
    113     retouch_info_t *r_info;
    114     prelink_info_t *p_info;
    115 
    116     int32_t target_offset = 0;
    117     if (desired_offset) target_offset = *desired_offset;
    118 
    119     int32_t p_offs = binary_size-sizeof(prelink_info_t); // prelink_info_t
    120     int32_t r_offs = p_offs-sizeof(retouch_info_t); // retouch_info_t
    121     int32_t b_offs; // retouch data blob
    122 
    123     // If not retouched, we say it was a match. This might get invoked on
    124     // non-retouched binaries, so that's why we need to do this.
    125     if (retouch_offset != NULL) *retouch_offset = target_offset;
    126     if (r_offs < 0) return (desired_offset == NULL) ?
    127                       RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
    128     p_info = (prelink_info_t *)(binary_object+p_offs);
    129     r_info = (retouch_info_t *)(binary_object+r_offs);
    130     if (strncmp(p_info->tag, "PRE ", 4) ||
    131         strncmp(r_info->tag, "RETOUCH ", 8))
    132         return (desired_offset == NULL) ?
    133           RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
    134 
    135     b_offs = r_offs-r_info->blob_size;
    136     if (b_offs < 0) {
    137         printf("negative binary offset: %d = %d - %d\n",
    138                b_offs, r_offs, r_info->blob_size);
    139         return RETOUCH_DATA_ERROR;
    140     }
    141     uint8_t *b_ptr = binary_object+b_offs;
    142 
    143     // Retouched: let's go through the work then.
    144     int32_t offset_candidate = target_offset;
    145     bool offset_set = false, offset_mismatch = false;
    146     init_compression_state();
    147     while (b_ptr < (uint8_t *)r_info) {
    148         int32_t retouch_entry_offset;
    149         uint32_t *retouch_entry;
    150         uint32_t retouch_original_value;
    151 
    152         b_ptr = decode_in_memory(b_ptr,
    153                                  &retouch_entry_offset,
    154                                  &retouch_original_value);
    155         if (retouch_entry_offset < (-1) ||
    156             retouch_entry_offset >= b_offs) {
    157             printf("bad retouch_entry_offset: %d", retouch_entry_offset);
    158             return RETOUCH_DATA_ERROR;
    159         }
    160 
    161         // "-1" means this is the value in prelink_info_t, which also gets
    162         // randomized.
    163         if (retouch_entry_offset == -1)
    164             retouch_entry = (uint32_t *)&(p_info->mmap_addr);
    165         else
    166             retouch_entry = (uint32_t *)(binary_object+retouch_entry_offset);
    167 
    168         if (desired_offset)
    169             *retouch_entry = retouch_original_value + target_offset;
    170 
    171         // Infer the randomization shift, compare to previously inferred.
    172         int32_t offset_of_this_entry = (int32_t)(*retouch_entry-
    173                                                  retouch_original_value);
    174         if (!offset_set) {
    175             offset_candidate = offset_of_this_entry;
    176             offset_set = true;
    177         } else {
    178             if (offset_candidate != offset_of_this_entry) {
    179                 offset_mismatch = true;
    180                 printf("offset is mismatched: %d, this entry is %d,"
    181                        " original 0x%x @ 0x%x",
    182                        offset_candidate, offset_of_this_entry,
    183                        retouch_original_value, retouch_entry_offset);
    184             }
    185         }
    186     }
    187     if (b_ptr > (uint8_t *)r_info) {
    188         printf("b_ptr went too far: %p, while r_info is %p",
    189                b_ptr, r_info);
    190         return RETOUCH_DATA_ERROR;
    191     }
    192 
    193     if (offset_mismatch) return RETOUCH_DATA_MISMATCHED;
    194     if (retouch_offset != NULL) *retouch_offset = offset_candidate;
    195     return RETOUCH_DATA_MATCHED;
    196 }
    197