1''' 2PROJECT: ReactOS baseaddress updater 3LICENSE: MIT (https://spdx.org/licenses/MIT) 4PURPOSE: Update baseaddresses of all modules 5COPYRIGHT: Copyright 2017,2018 Mark Jansen (mark.jansen@reactos.org) 6''' 7 8from __future__ import print_function, absolute_import, division 9 10USAGE = """ 11This script will update the baseaddresses of all modules, based on the build output. 12 13Specify the build output dir as commandline argument to the script: 14`python gen_baseaddress.py C:\\Users\\Mark\\reactos\\output-MinGW-i386` 15 16Multiple directories can be specified: 17`python gen_baseaddress r:/build/msvc r:/build/gcc` 18""" 19 20import os 21import struct 22import sys 23 24try: 25 import pefile 26except ImportError: 27 print('# Please install pefile from pip or https://github.com/erocarrera/pefile') 28 sys.exit(-1) 29 30 31ALL_EXTENSIONS = ( 32 '.dll', '.acm', '.ax', '.cpl', '.drv', '.ocx' 33) 34 35PRIORITIES = ( 36 'ntdll.dll', 37 'kernel32.dll', 38 'msvcrt.dll', 39 'advapi32.dll', 40 'gdi32.dll', 41 'user32.dll', 42 'dhcpcsvc.dll', 43 'dnsapi.dll', 44 'icmp.dll', 45 'iphlpapi.dll', 46 'ws2_32.dll', 47 'ws2help.dll', 48 'shlwapi.dll', 49 'rpcrt4.dll', 50 'comctl32.dll', 51 'ole32.dll', 52 'winspool.drv', 53 'winmm.dll', 54 'comdlg32.dll', 55 'shell32.dll', 56 'lz32.dll', 57 'version.dll', 58 'oleaut32.dll', 59 'setupapi.dll', 60 'mpr.dll', 61 'crypt32.dll', 62 'wininet.dll', 63 'urlmon.dll', 64 'psapi.dll', 65 'imm32.dll', 66 'msvfw32.dll', 67 'dbghelp.dll', 68 'devmgr.dll', 69 'msacm32.dll', 70 'netapi32.dll', 71 'powrprof.dll', 72 'secur32.dll', 73 'wintrust.dll', 74 'avicap32.dll', 75 'cabinet.dll', 76 'dsound.dll', 77 'glu32.dll', 78 'opengl32.dll', 79 'riched20.dll', 80 'smdll.dll', 81 'userenv.dll', 82 'uxtheme.dll', 83 'cryptui.dll', 84 'csrsrv.dll', 85 'basesrv.dll', 86 'winsrv.dll', 87 'dplayx.dll', 88 'gdiplus.dll', 89 'msimg32.dll', 90 'mswsock.dll', 91 'oledlg.dll', 92 'rasapi32.dll', 93 'rsaenh.dll', 94 'samlib.dll', 95 'sensapi.dll', 96 'sfc_os.dll', 97 'snmpapi.dll', 98 'spoolss.dll', 99 'usp10.dll', 100) 101 102EXCLUDE = ( 103 'bmfd.dll', 104 'bootvid.dll', 105 'framebuf.dll', 106 'framebuf_new.dll', 107 'ftfd.dll', 108 'fusion.dll', 109 'genincdata.dll', 110 'hal.dll', 111 'halaacpi.dll', 112 'halacpi.dll', 113 'halapic.dll', 114 'kbda1.dll', 115 'kbda2.dll', 116 'kbda3.dll', 117 'kbdal.dll', 118 'kbdarme.dll', 119 'kbdarmw.dll', 120 'kbdaze.dll', 121 'kbdazel.dll', 122 'kbdbe.dll', 123 'kbdbga.dll', 124 'kbdbgm.dll', 125 'kbdbgt.dll', 126 'kbdblr.dll', 127 'kbdbr.dll', 128 'kbdbur.dll', 129 'kbdcan.dll', 130 'kbdcr.dll', 131 'kbdcz.dll', 132 'kbdcz1.dll', 133 'kbdda.dll', 134 'kbddv.dll', 135 'kbdes.dll', 136 'kbdest.dll', 137 'kbdfc.dll', 138 'kbdfi.dll', 139 'kbdfr.dll', 140 'kbdgeo.dll', 141 'kbdgerg.dll', 142 'kbdgneo.dll', 143 'kbdgr.dll', 144 'kbdgrist.dll', 145 'kbdhe.dll', 146 'kbdheb.dll', 147 'kbdhu.dll', 148 'kbdic.dll', 149 'kbdinasa.dll', 150 'kbdinben.dll', 151 'kbdindev.dll', 152 'kbdinguj.dll', 153 'kbdinmal.dll', 154 'kbdir.dll', 155 'kbdit.dll', 156 'kbdja.dll', 157 'kbdkaz.dll', 158 'kbdko.dll', 159 'kbdla.dll', 160 'kbdlt1.dll', 161 'kbdlv.dll', 162 'kbdmac.dll', 163 'kbdne.dll', 164 'kbdno.dll', 165 'kbdpl.dll', 166 'kbdpl1.dll', 167 'kbdpo.dll', 168 'kbdro.dll', 169 'kbdru.dll', 170 'kbdru1.dll', 171 'kbdsg.dll', 172 'kbdsk.dll', 173 'kbdsk1.dll', 174 'kbdsw.dll', 175 'kbdtat.dll', 176 'kbdth0.dll', 177 'kbdth1.dll', 178 'kbdth2.dll', 179 'kbdth3.dll', 180 'kbdtuf.dll', 181 'kbdtuq.dll', 182 'kbduk.dll', 183 'kbdur.dll', 184 'kbdurs.dll', 185 'kbdus.dll', 186 'kbdusa.dll', 187 'kbdusl.dll', 188 'kbdusr.dll', 189 'kbdusx.dll', 190 'kbduzb.dll', 191 'kbdvntc.dll', 192 'kbdycc.dll', 193 'kbdycl.dll', 194 'kdcom.dll', 195 'kdvbox.dll', 196 'vgaddi.dll', 197 'dllexport_test_dll1.dll', 198 'dllexport_test_dll2.dll', 199 'dllimport_test.dll', 200 'MyEventProvider.dll', 201 'w32kdll_2k3sp2.dll', 202 'w32kdll_ros.dll', 203 'w32kdll_xpsp2.dll', 204) 205 206IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b 207IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b 208 209IMAGE_TYPES = { 210 IMAGE_NT_OPTIONAL_HDR32_MAGIC: 0, 211 IMAGE_NT_OPTIONAL_HDR64_MAGIC: 0 212} 213 214def is_x64(): 215 return IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR64_MAGIC] > IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR32_MAGIC] 216 217def size_of_image(filename): 218 with open(filename, 'rb') as fin: 219 if fin.read(2) != b'MZ': 220 print(filename, 'No dos header found!') 221 return 0 222 fin.seek(0x3C) 223 e_lfanew = struct.unpack('i', fin.read(4))[0] 224 fin.seek(e_lfanew) 225 if fin.read(4) != b'PE\0\0': 226 print(filename, 'No PE header found!') 227 return 0 228 fin.seek(e_lfanew + 0x18) 229 pe_magic = struct.unpack('h', fin.read(2))[0] 230 if pe_magic in IMAGE_TYPES.keys(): 231 IMAGE_TYPES[pe_magic] += 1 232 fin.seek(e_lfanew + 0x50) 233 pe_size_of_image = struct.unpack('i', fin.read(4))[0] 234 return pe_size_of_image 235 print(filename, 'Unknown executable format!') 236 return 0 237 238 239class Module(object): 240 def __init__(self, name, address, size, filename): 241 self._name = name 242 self.address = address 243 self.size = size 244 self._reserved = address != 0 245 self.filename = filename 246 247 def gen_baseaddress(self, output_file): 248 name, ext = os.path.splitext(self._name) 249 postfix = '' 250 if ext in('.acm', '.drv') and self._name != 'winspool.drv': 251 name = self._name 252 if name == 'ntdll': 253 postfix = ' # should be above 0x%08x' % self.address 254 elif self._reserved: 255 postfix = ' # reserved' 256 output_file.write('set(baseaddress_%-30s 0x%08x)%s\n' % (name, self.address, postfix)) 257 258 def end(self): 259 return self.address + self.size 260 261 def __repr__(self): 262 return '%s (0x%08x - 0x%08x)' % (self._name, self.address, self.end()) 263 264class MemoryLayout(object): 265 def __init__(self, startaddress): 266 self.addresses = [] 267 self.found = {} 268 self.reserved = {} 269 self.initial = startaddress 270 self.start_at = 0 271 self.module_padding = 0x2000 272 273 def add_reserved(self, name, address): 274 self.reserved[name] = (address, 0) 275 276 def add(self, filename, name): 277 size = size_of_image(filename) 278 addr = 0 279 if name in self.found: 280 return # Assume duplicate files (rshell, ...) are 1:1 copies 281 if name in self.reserved: 282 addr = self.reserved[name][0] 283 self.reserved[name] = (addr, size) 284 self.found[name] = Module(name, addr, size, filename) 285 286 def _next_address(self, size): 287 if self.start_at: 288 addr = (self.start_at - size - self.module_padding - 0xffff) & 0xffff0000 289 self.start_at = addr 290 else: 291 addr = self.start_at = self.initial 292 return addr 293 294 def next_address(self, size): 295 while True: 296 current_start = self._next_address(size) 297 current_end = current_start + size + self.module_padding 298 # Is there overlap with reserved modules? 299 for key, reserved in self.reserved.items(): 300 res_start = reserved[0] 301 res_end = res_start + reserved[1] + self.module_padding 302 if (res_start <= current_start <= res_end) or \ 303 (res_start <= current_end <= res_end) or \ 304 (current_start < res_start and current_end > res_end): 305 # We passed this reserved item, we can remove it now 306 self.start_at = min(res_start, current_start) 307 del self.reserved[key] 308 current_start = 0 309 break 310 # No overlap with a reserved module? 311 if current_start: 312 return current_start 313 314 def update(self, priorities): 315 # sort addresses, should only contain reserved modules at this point! 316 for key, reserved in self.reserved.items(): 317 assert reserved[1] != 0, key 318 for curr in priorities: 319 if not curr in self.found: 320 print('# Did not find', curr, '!') 321 else: 322 obj = self.found[curr] 323 del self.found[curr] 324 if not obj.address: 325 obj.address = self.next_address(obj.size) 326 self.addresses.append(obj) 327 # We handled all known modules now, run over the rest we found 328 for key in sorted(self.found): 329 obj = self.found[key] 330 obj.address = self.next_address(obj.size) 331 self.addresses.append(obj) 332 333 def gen_baseaddress(self, output_file): 334 for obj in self.addresses: 335 obj.gen_baseaddress(output_file) 336 337def get_target_file(ntdll_path): 338 if 'pefile' in globals(): 339 ntdll_pe = pefile.PE(ntdll_path, fast_load=True) 340 names = [sect.Name.strip(b'\0') for sect in ntdll_pe.sections] 341 count = b'|'.join(names).count(b'/') 342 if b'.rossym' in names: 343 return 'baseaddress.cmake' 344 elif is_x64(): 345 return 'baseaddress_msvc_x64.cmake' 346 elif count == 0: 347 return 'baseaddress_msvc.cmake' 348 elif count > 3: 349 return 'baseaddress_dwarf.cmake' 350 else: 351 assert False, "Unknown" 352 return None 353 354def run_dir(target): 355 layout = MemoryLayout(0x7c920000) 356 layout.add_reserved('user32.dll', 0x77a20000) 357 IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR64_MAGIC] = 0 358 IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR32_MAGIC] = 0 359 for root, _, files in os.walk(target): 360 for dll in [filename for filename in files if filename.endswith(ALL_EXTENSIONS)]: 361 if not dll in EXCLUDE and not dll.startswith('api-ms-win-'): 362 layout.add(os.path.join(root, dll), dll) 363 ntdll_path = layout.found['ntdll.dll'].filename 364 target_file = get_target_file(ntdll_path) 365 if target_file: 366 target_dir = os.path.realpath(os.path.dirname(os.path.dirname(__file__))) 367 target_path = os.path.join(target_dir, 'cmake', target_file) 368 output_file = open(target_path, "w") 369 else: 370 output_file = sys.stdout 371 with output_file: 372 output_file.write('# Generated from {}\n'.format(target)) 373 output_file.write('# Generated by sdk/tools/gen_baseaddress.py\n\n') 374 layout.update(PRIORITIES) 375 layout.gen_baseaddress(output_file) 376 377def main(): 378 dirs = sys.argv[1:] 379 if len(dirs) < 1: 380 trydir = os.getcwd() 381 print(USAGE) 382 print('No path specified, trying the working directory: ', trydir) 383 dirs = [trydir] 384 for onedir in dirs: 385 if onedir.lower() in ['-help', '/help', '/h', '-h', '/?', '-?']: 386 print(USAGE) 387 else: 388 run_dir(onedir) 389 390 391if __name__ == '__main__': 392 main() 393