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