Add r2r checker (#355)
[platform/core/dotnet/launcher.git] / NativeLauncher / tool / r2r_checker.cc
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
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 <fcntl.h>
18 #include <stdio.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21
22 #include <pal.h>
23 #include <pal_endian.h>
24 #include <ntimage.h>
25
26 #include <inc/corhdr.h>
27 #include <inc/readytorun.h>
28
29 #include <log.h>
30 #include <r2r_checker.h>
31
32 static inline UINT AlignUp(UINT value, UINT alignment)
33 {
34         return (value + alignment - 1) & ~(alignment - 1);
35 }
36
37 static inline UINT64 AlignUp(UINT64 value, UINT alignment)
38 {
39         return (value + alignment - 1)& ~(UINT64)(alignment - 1);
40 }
41
42 static IMAGE_NT_HEADERS* getNTHeaders(void* pAddr)
43 {
44         IMAGE_DOS_HEADER* pDOS = (IMAGE_DOS_HEADER*)pAddr;
45         if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)) return NULL;
46
47         return (IMAGE_NT_HEADERS*)((ULONG_PTR)pAddr + VAL32(pDOS->e_lfanew));
48 }
49
50 static IMAGE_DATA_DIRECTORY* getCorDirectoryEntry(IMAGE_NT_HEADERS* pNTHeaders)
51 {
52         IMAGE_DATA_DIRECTORY* pDir = NULL;
53         if (pNTHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
54                 pDir = (IMAGE_DATA_DIRECTORY*)((ULONG_PTR)(pNTHeaders)
55                                 + offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory)
56                                 + IMAGE_DIRECTORY_ENTRY_COMHEADER * sizeof(IMAGE_DATA_DIRECTORY));
57         } else if (pNTHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
58                 pDir = (IMAGE_DATA_DIRECTORY*)((ULONG_PTR)(pNTHeaders)
59                                 + offsetof(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory)
60                                 + IMAGE_DIRECTORY_ENTRY_COMHEADER * sizeof(IMAGE_DATA_DIRECTORY));
61         } else {
62                 _SERR("Invalid Optional Header Magic Number: 0x%x", VAL16(pNTHeaders->OptionalHeader.Magic));
63         }
64
65         return pDir;
66 }
67
68 static IMAGE_SECTION_HEADER* findFirstSection(IMAGE_NT_HEADERS* pNTHeaders)
69 {
70         return (IMAGE_SECTION_HEADER*)(
71                         (ULONG_PTR)(pNTHeaders) +
72                         offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
73                         VAL16(pNTHeaders->FileHeader.SizeOfOptionalHeader));
74 }
75
76 static IMAGE_SECTION_HEADER* getSection(IMAGE_NT_HEADERS* pNTHeaders, UINT rva) {
77         IMAGE_SECTION_HEADER* section = (IMAGE_SECTION_HEADER*)(findFirstSection(pNTHeaders));
78         IMAGE_SECTION_HEADER* sectionEnd = section + VAL16(pNTHeaders->FileHeader.NumberOfSections);
79         while (section < sectionEnd) {
80                 if (rva < (VAL32(section->VirtualAddress)
81                                         + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(pNTHeaders->OptionalHeader.SectionAlignment)))) {
82                         if (rva < VAL32(section->VirtualAddress)) {
83                                 return NULL;
84                         } else {
85                                 return section;
86                         }
87                 }
88                 section++;
89         }
90
91         return NULL;
92 }
93
94 static ULONG_PTR getDirectoryData(void* pBaseAddr, IMAGE_NT_HEADERS* pNTHeaders, IMAGE_DATA_DIRECTORY* pDir) {
95         UINT rva = pDir->VirtualAddress;
96         if (rva == 0) {
97                 return 0;
98         }
99
100         IMAGE_SECTION_HEADER* section = getSection(pNTHeaders, rva);
101         if (!section) {
102                 return 0;
103         }
104
105         return (ULONG_PTR)pBaseAddr + rva - VAL32(section->VirtualAddress) + VAL32(section->PointerToRawData);
106 }
107
108 static bool hasValidR2RHeader(void* pAddr)
109 {
110         IMAGE_NT_HEADERS* pNTHeaders = getNTHeaders(pAddr);
111         if (!pNTHeaders) {
112                 _SERR("Invalid NT Header");
113                 return false;
114         }
115
116         IMAGE_DATA_DIRECTORY* pCorDir = getCorDirectoryEntry(pNTHeaders);
117         if (!pCorDir) {
118                 _SERR("Invalid Cor Data Directory");
119                 return false;
120         }
121
122         IMAGE_COR20_HEADER* pCor = (IMAGE_COR20_HEADER*)getDirectoryData(pAddr, pNTHeaders, pCorDir);
123         if (!pCor) {
124                 _SERR("Invalid Cor Header");
125                 return false;
126         }
127
128         IMAGE_DATA_DIRECTORY* pR2RDir = &pCor->ManagedNativeHeader;
129         if (VAL32(pR2RDir->Size) >= sizeof(READYTORUN_HEADER)) {
130                 READYTORUN_HEADER* pR2RHeader = (READYTORUN_HEADER*)getDirectoryData(pAddr, pNTHeaders, pR2RDir);
131                 if (!pR2RHeader) {
132                         return false;
133                 }
134
135                 if (pR2RHeader->Signature != VAL16(READYTORUN_SIGNATURE)) {
136                         return false;
137                 }
138                 return true;
139         }
140         return false;
141 }
142
143 bool isR2RImage(std::string fileName)
144 {
145         int fd;
146         struct stat sb;
147         if ((fd = open(fileName.c_str(), O_RDONLY)) == -1) {
148                 _SERR("File Not Found: %s", fileName.c_str());
149                 return false;
150         }
151
152         if (fstat(fd, &sb) == -1) {
153                 return false;
154         }
155
156         void* pAddr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
157         if (pAddr == MAP_FAILED) {
158                 _SERR("Fail to Map File: %s", fileName.c_str());
159                 close(fd);
160                 return false;
161         }
162
163         bool ret = hasValidR2RHeader(pAddr);
164
165         munmap(pAddr, sb.st_size);
166         close(fd);
167         return ret;
168 }