reactos/sdk/tools/gen_baseaddress.py

393 lines
11 KiB
Python
Raw Normal View History

'''
PROJECT: ReactOS baseaddress updater
LICENSE: MIT (https://spdx.org/licenses/MIT)
PURPOSE: Update baseaddresses of all modules
COPYRIGHT: Copyright 2017,2018 Mark Jansen (mark.jansen@reactos.org)
'''
from __future__ import print_function, absolute_import, division
USAGE = """
This script will update the baseaddresses of all modules, based on the build output.
Specify the build output dir as commandline argument to the script:
`python gen_baseaddress.py C:\\Users\\Mark\\reactos\\output-MinGW-i386`
Multiple directories can be specified:
`python gen_baseaddress r:/build/msvc r:/build/gcc`
"""
import os
import struct
import sys
try:
import pefile
except ImportError:
print('# Please install pefile from pip or https://github.com/erocarrera/pefile')
sys.exit(-1)
ALL_EXTENSIONS = (
'.dll', '.acm', '.ax', '.cpl', '.drv', '.ocx'
)
PRIORITIES = (
'ntdll.dll',
'kernel32.dll',
'msvcrt.dll',
'advapi32.dll',
'gdi32.dll',
'user32.dll',
'dhcpcsvc.dll',
'dnsapi.dll',
'icmp.dll',
'iphlpapi.dll',
'ws2_32.dll',
'ws2help.dll',
'shlwapi.dll',
'rpcrt4.dll',
'comctl32.dll',
'ole32.dll',
'winspool.drv',
'winmm.dll',
'comdlg32.dll',
'shell32.dll',
'lz32.dll',
'version.dll',
'oleaut32.dll',
'setupapi.dll',
'mpr.dll',
'crypt32.dll',
'wininet.dll',
'urlmon.dll',
'psapi.dll',
'imm32.dll',
'msvfw32.dll',
'dbghelp.dll',
'devmgr.dll',
'msacm32.dll',
'netapi32.dll',
'powrprof.dll',
'secur32.dll',
'wintrust.dll',
'avicap32.dll',
'cabinet.dll',
'dsound.dll',
'glu32.dll',
'opengl32.dll',
'riched20.dll',
'smdll.dll',
'userenv.dll',
'uxtheme.dll',
'cryptui.dll',
'csrsrv.dll',
'basesrv.dll',
'winsrv.dll',
'dplayx.dll',
'gdiplus.dll',
'msimg32.dll',
'mswsock.dll',
'oledlg.dll',
'rasapi32.dll',
'rsaenh.dll',
'samlib.dll',
'sensapi.dll',
'sfc_os.dll',
'snmpapi.dll',
'spoolss.dll',
'usp10.dll',
)
EXCLUDE = (
'bmfd.dll',
'bootvid.dll',
'framebuf.dll',
'framebuf_new.dll',
'ftfd.dll',
'fusion.dll',
'genincdata.dll',
'hal.dll',
'halaacpi.dll',
'halacpi.dll',
'halapic.dll',
'kbda1.dll',
'kbda2.dll',
'kbda3.dll',
'kbdal.dll',
'kbdarme.dll',
'kbdarmw.dll',
'kbdaze.dll',
'kbdazel.dll',
'kbdbe.dll',
'kbdbga.dll',
'kbdbgm.dll',
'kbdbgt.dll',
'kbdblr.dll',
'kbdbr.dll',
'kbdbur.dll',
'kbdcan.dll',
'kbdcr.dll',
'kbdcz.dll',
'kbdcz1.dll',
'kbdda.dll',
'kbddv.dll',
'kbdes.dll',
'kbdest.dll',
'kbdfc.dll',
'kbdfi.dll',
'kbdfr.dll',
'kbdgeo.dll',
'kbdgerg.dll',
'kbdgneo.dll',
'kbdgr.dll',
'kbdgrist.dll',
'kbdhe.dll',
'kbdheb.dll',
'kbdhu.dll',
'kbdic.dll',
'kbdinasa.dll',
'kbdinben.dll',
'kbdindev.dll',
'kbdinguj.dll',
'kbdinmal.dll',
'kbdir.dll',
'kbdit.dll',
'kbdja.dll',
'kbdkaz.dll',
'kbdko.dll',
'kbdla.dll',
'kbdlt1.dll',
'kbdlv.dll',
'kbdmac.dll',
'kbdne.dll',
'kbdno.dll',
'kbdpl.dll',
'kbdpl1.dll',
'kbdpo.dll',
'kbdro.dll',
'kbdru.dll',
'kbdru1.dll',
'kbdsg.dll',
'kbdsk.dll',
'kbdsk1.dll',
'kbdsw.dll',
'kbdtat.dll',
'kbdth0.dll',
'kbdth1.dll',
'kbdth2.dll',
'kbdth3.dll',
'kbdtuf.dll',
'kbdtuq.dll',
'kbduk.dll',
'kbdur.dll',
'kbdurs.dll',
'kbdus.dll',
'kbdusa.dll',
'kbdusl.dll',
'kbdusr.dll',
'kbdusx.dll',
'kbduzb.dll',
'kbdvntc.dll',
'kbdycc.dll',
'kbdycl.dll',
'kdcom.dll',
'kdvbox.dll',
'vgaddi.dll',
'dllexport_test_dll1.dll',
'dllexport_test_dll2.dll',
'dllimport_test.dll',
'MyEventProvider.dll',
'w32kdll_2k3sp2.dll',
'w32kdll_ros.dll',
'w32kdll_xpsp2.dll',
)
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
IMAGE_TYPES = {
IMAGE_NT_OPTIONAL_HDR32_MAGIC: 0,
IMAGE_NT_OPTIONAL_HDR64_MAGIC: 0
}
def is_x64():
return IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR64_MAGIC] > IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR32_MAGIC]
def size_of_image(filename):
with open(filename, 'rb') as fin:
if fin.read(2) != b'MZ':
print(filename, 'No dos header found!')
return 0
fin.seek(0x3C)
e_lfanew = struct.unpack('i', fin.read(4))[0]
fin.seek(e_lfanew)
if fin.read(4) != b'PE\0\0':
print(filename, 'No PE header found!')
return 0
fin.seek(e_lfanew + 0x18)
pe_magic = struct.unpack('h', fin.read(2))[0]
if pe_magic in IMAGE_TYPES.keys():
IMAGE_TYPES[pe_magic] += 1
fin.seek(e_lfanew + 0x50)
pe_size_of_image = struct.unpack('i', fin.read(4))[0]
return pe_size_of_image
print(filename, 'Unknown executable format!')
return 0
class Module(object):
def __init__(self, name, address, size, filename):
self._name = name
self.address = address
self.size = size
self._reserved = address != 0
self.filename = filename
def gen_baseaddress(self, output_file):
name, ext = os.path.splitext(self._name)
postfix = ''
if ext in('.acm', '.drv') and self._name != 'winspool.drv':
name = self._name
if name == 'ntdll':
postfix = ' # should be above 0x%08x' % self.address
elif self._reserved:
postfix = ' # reserved'
output_file.write('set(baseaddress_%-30s 0x%08x)%s\n' % (name, self.address, postfix))
def end(self):
return self.address + self.size
def __repr__(self):
return '%s (0x%08x - 0x%08x)' % (self._name, self.address, self.end())
class MemoryLayout(object):
def __init__(self, startaddress):
self.addresses = []
self.found = {}
self.reserved = {}
self.initial = startaddress
self.start_at = 0
self.module_padding = 0x2000
def add_reserved(self, name, address):
self.reserved[name] = (address, 0)
def add(self, filename, name):
size = size_of_image(filename)
addr = 0
if name in self.found:
return # Assume duplicate files (rshell, ...) are 1:1 copies
if name in self.reserved:
addr = self.reserved[name][0]
self.reserved[name] = (addr, size)
self.found[name] = Module(name, addr, size, filename)
def _next_address(self, size):
if self.start_at:
addr = (self.start_at - size - self.module_padding - 0xffff) & 0xffff0000
self.start_at = addr
else:
addr = self.start_at = self.initial
return addr
def next_address(self, size):
while True:
current_start = self._next_address(size)
current_end = current_start + size + self.module_padding
# Is there overlap with reserved modules?
for key, reserved in self.reserved.items():
res_start = reserved[0]
res_end = res_start + reserved[1] + self.module_padding
if (res_start <= current_start <= res_end) or \
(res_start <= current_end <= res_end) or \
(current_start < res_start and current_end > res_end):
# We passed this reserved item, we can remove it now
self.start_at = min(res_start, current_start)
del self.reserved[key]
current_start = 0
break
# No overlap with a reserved module?
if current_start:
return current_start
def update(self, priorities):
# sort addresses, should only contain reserved modules at this point!
for key, reserved in self.reserved.items():
assert reserved[1] != 0, key
for curr in priorities:
if not curr in self.found:
print('# Did not find', curr, '!')
else:
obj = self.found[curr]
del self.found[curr]
if not obj.address:
obj.address = self.next_address(obj.size)
self.addresses.append(obj)
# We handled all known modules now, run over the rest we found
for key in sorted(self.found):
obj = self.found[key]
obj.address = self.next_address(obj.size)
self.addresses.append(obj)
def gen_baseaddress(self, output_file):
for obj in self.addresses:
obj.gen_baseaddress(output_file)
def get_target_file(ntdll_path):
if 'pefile' in globals():
ntdll_pe = pefile.PE(ntdll_path, fast_load=True)
names = [sect.Name.strip(b'\0') for sect in ntdll_pe.sections]
count = b'|'.join(names).count(b'/')
if b'.rossym' in names:
return 'baseaddress.cmake'
elif is_x64():
return 'baseaddress_msvc_x64.cmake'
elif count == 0:
return 'baseaddress_msvc.cmake'
elif count > 3:
return 'baseaddress_dwarf.cmake'
else:
assert False, "Unknown"
return None
def run_dir(target):
layout = MemoryLayout(0x7c920000)
layout.add_reserved('user32.dll', 0x77a20000)
IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR64_MAGIC] = 0
IMAGE_TYPES[IMAGE_NT_OPTIONAL_HDR32_MAGIC] = 0
for root, _, files in os.walk(target):
for dll in [filename for filename in files if filename.endswith(ALL_EXTENSIONS)]:
if not dll in EXCLUDE and not dll.startswith('api-ms-win-'):
layout.add(os.path.join(root, dll), dll)
ntdll_path = layout.found['ntdll.dll'].filename
target_file = get_target_file(ntdll_path)
if target_file:
target_dir = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
target_path = os.path.join(target_dir, 'cmake', target_file)
output_file = open(target_path, "w")
else:
output_file = sys.stdout
with output_file:
output_file.write('# Generated from {}\n'.format(target))
output_file.write('# Generated by sdk/tools/gen_baseaddress.py\n\n')
layout.update(PRIORITIES)
layout.gen_baseaddress(output_file)
def main():
dirs = sys.argv[1:]
if len(dirs) < 1:
trydir = os.getcwd()
print(USAGE)
print('No path specified, trying the working directory: ', trydir)
dirs = [trydir]
for onedir in dirs:
if onedir.lower() in ['-help', '/help', '/h', '-h', '/?', '-?']:
print(USAGE)
else:
run_dir(onedir)
if __name__ == '__main__':
main()