Pack 16bit code into last part of f-segment.
[seabios.git] / tools / layoutrom.py
1 #!/usr/bin/env python
2 # Script to arrange sections to ensure fixed offsets.
3 #
4 # Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
5 #
6 # This file may be distributed under the terms of the GNU GPLv3 license.
7
8 import sys
9
10 def main():
11     # Get output name
12     outname = sys.argv[1]
13
14     # Read in section names and sizes
15     # sections = [(size, align, name), ...]
16     sections = []
17     for line in sys.stdin.readlines():
18         try:
19             idx, name, size, vma, lma, fileoff, align = line.split()
20             if align[:3] != '2**':
21                 continue
22             sections.append((int(size, 16), 2**int(align[3:]), name))
23         except:
24             pass
25
26     doLayout(sections, outname)
27
28 def alignpos(pos, alignbytes):
29     mask = alignbytes - 1
30     return (pos + mask) & ~mask
31
32 MAXPOS = 0x10000
33
34 def outsection(file, name):
35     file.write("*(%s)\n" % (name,))
36
37 def doLayout(sections, outname):
38     textsections = []
39     rodatasections = []
40     datasections = []
41     # fixedsections = [(addr, sectioninfo, extasectionslist), ...]
42     fixedsections = []
43     # canrelocate = [(sectioninfo, list), ...]
44     canrelocate = []
45
46     # Find desired sections.
47     for section in sections:
48         size, align, name = section
49         if name[:11] == '.fixedaddr.':
50             addr = int(name[11:], 16)
51             fixedsections.append((addr, section, []))
52             if align != 1:
53                 print "Error: Fixed section %s has non-zero alignment (%d)" % (
54                     name, align)
55                 sys.exit(1)
56         if name[:6] == '.text.':
57             textsections.append(section)
58             canrelocate.append((section, textsections))
59         if name[:17] == '.rodata.__func__.' or name == '.rodata.str1.1':
60             rodatasections.append(section)
61             #canrelocate.append((section, rodatasections))
62         if name[:8] == '.data16.':
63             datasections.append(section)
64             #canrelocate.append((section, datasections))
65
66     # Find freespace in fixed address area
67     fixedsections.sort()
68     # fixedAddr = [(freespace, sectioninfo), ...]
69     fixedAddr = []
70     for i in range(len(fixedsections)):
71         fixedsectioninfo = fixedsections[i]
72         addr, section, extrasectionslist = fixedsectioninfo
73         if i == len(fixedsections) - 1:
74             nextaddr = MAXPOS
75         else:
76             nextaddr = fixedsections[i+1][0]
77         avail = nextaddr - addr - section[0]
78         fixedAddr.append((avail, fixedsectioninfo))
79
80     # Attempt to fit other sections into fixed area
81     fixedAddr.sort()
82     canrelocate.sort()
83     totalused = 0
84     for freespace, fixedsectioninfo in fixedAddr:
85         fixedaddr, fixedsection, extrasections = fixedsectioninfo
86         addpos = fixedaddr + fixedsection[0]
87         totalused += fixedsection[0]
88         nextfixedaddr = addpos + freespace
89 #        print "Filling section %x uses %d, next=%x, available=%d" % (
90 #            fixedaddr, fixedsection[0], nextfixedaddr, freespace)
91         while 1:
92             canfit = None
93             for fixedaddrinfo in canrelocate:
94                 fitsection, inlist = fixedaddrinfo
95                 fitsize, fitalign, fitname = fitsection
96                 if addpos + fitsize > nextfixedaddr:
97                     # Can't fit and nothing else will fit.
98                     break
99                 fitnextaddr = alignpos(addpos, fitalign) + fitsize
100 #                print "Test %s - %x vs %x" % (
101 #                    fitname, fitnextaddr, nextfixedaddr)
102                 if fitnextaddr > nextfixedaddr:
103                     # This item can't fit.
104                     continue
105                 canfit = (fitnextaddr, fixedaddrinfo)
106             if canfit is None:
107                 break
108             # Found a section that can fit.
109             fitnextaddr, fixedaddrinfo = canfit
110             canrelocate.remove(fixedaddrinfo)
111             fitsection, inlist = fixedaddrinfo
112             inlist.remove(fitsection)
113             extrasections.append(fitsection)
114             addpos = fitnextaddr
115             totalused += fitsection[0]
116 #            print "    Adding %s (size %d align %d) pos=%x avail=%d" % (
117 #                fitsection[2], fitsection[0], fitsection[1]
118 #                , fitnextaddr, nextfixedaddr - fitnextaddr)
119     firstfixed = fixedsections[0][0]
120
121     # Find overall start position
122     restalign = 0
123     restspace = 0
124     restsections = []
125     for section in textsections + rodatasections + datasections:
126         size, align, name = section
127         if align > restalign:
128             restalign = align
129         restspace = alignpos(restspace, align) + size
130         restsections.append(section)
131     startrest = (firstfixed - restspace) / restalign * restalign
132
133     # Report stats
134     total = MAXPOS-firstfixed
135     slack = total - totalused
136     print ("Fixed space: 0x%x-0x%x  total: %d  slack: %d"
137            "  Percent slack: %.1f%%" % (
138             firstfixed, MAXPOS, total, slack,
139             (float(slack) / total) * 100.0))
140
141     # Write header
142     output = open(outname, 'wb')
143     output.write("""
144         .text16 0x%x : {
145                 code16_start = ABSOLUTE(.) ;
146                 freespace_end = . ;
147 """ % startrest)
148
149     # Write regular sections
150     for section in restsections:
151         name = section[2]
152         if name == rodatasections[0][2]:
153             output.write("code16_rodata = . ;\n")
154         outsection(output, name)
155
156     # Write fixed sections
157     for addr, section, extrasections in fixedsections:
158         name = section[2]
159         output.write(". = ( 0x%x - code16_start ) ;\n" % (addr,))
160         output.write("*(%s)\n" % (name,))
161         for extrasection in extrasections:
162             outsection(output, extrasection[2])
163
164     # Write trailer
165     output.write("""
166                 code16_end = ABSOLUTE(.) ;
167         }
168 """)
169
170
171 if __name__ == '__main__':
172     main()