2 # Script to arrange sections to ensure fixed offsets.
4 # Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
6 # This file may be distributed under the terms of the GNU GPLv3 license.
11 def alignpos(pos, alignbytes):
13 return (pos + mask) & ~mask
16 ######################################################################
17 # 16bit fixed address section fitting
18 ######################################################################
22 def doLayout16(sections, outname):
26 # fixedsections = [(addr, sectioninfo, extasectionslist), ...]
28 # canrelocate = [(sectioninfo, list), ...]
31 # Find desired sections.
32 for section in sections:
33 size, align, name = section
34 if name[:11] == '.fixedaddr.':
35 addr = int(name[11:], 16)
36 fixedsections.append((addr, section, []))
38 print "Error: Fixed section %s has non-zero alignment (%d)" % (
41 if name[:6] == '.text.':
42 textsections.append(section)
43 canrelocate.append((section, textsections))
44 if name[:17] == '.rodata.__func__.' or name == '.rodata.str1.1':
45 rodatasections.append(section)
46 #canrelocate.append((section, rodatasections))
47 if name[:8] == '.data16.':
48 datasections.append(section)
49 #canrelocate.append((section, datasections))
51 # Find freespace in fixed address area
53 # fixedAddr = [(freespace, sectioninfo), ...]
55 for i in range(len(fixedsections)):
56 fixedsectioninfo = fixedsections[i]
57 addr, section, extrasectionslist = fixedsectioninfo
58 if i == len(fixedsections) - 1:
61 nextaddr = fixedsections[i+1][0]
62 avail = nextaddr - addr - section[0]
63 fixedAddr.append((avail, fixedsectioninfo))
65 # Attempt to fit other sections into fixed area
69 for freespace, fixedsectioninfo in fixedAddr:
70 fixedaddr, fixedsection, extrasections = fixedsectioninfo
71 addpos = fixedaddr + fixedsection[0]
72 totalused += fixedsection[0]
73 nextfixedaddr = addpos + freespace
74 # print "Filling section %x uses %d, next=%x, available=%d" % (
75 # fixedaddr, fixedsection[0], nextfixedaddr, freespace)
78 for fixedaddrinfo in canrelocate:
79 fitsection, inlist = fixedaddrinfo
80 fitsize, fitalign, fitname = fitsection
81 if addpos + fitsize > nextfixedaddr:
82 # Can't fit and nothing else will fit.
84 fitnextaddr = alignpos(addpos, fitalign) + fitsize
85 # print "Test %s - %x vs %x" % (
86 # fitname, fitnextaddr, nextfixedaddr)
87 if fitnextaddr > nextfixedaddr:
88 # This item can't fit.
90 canfit = (fitnextaddr, fixedaddrinfo)
93 # Found a section that can fit.
94 fitnextaddr, fixedaddrinfo = canfit
95 canrelocate.remove(fixedaddrinfo)
96 fitsection, inlist = fixedaddrinfo
97 inlist.remove(fitsection)
98 extrasections.append(fitsection)
100 totalused += fitsection[0]
101 # print " Adding %s (size %d align %d) pos=%x avail=%d" % (
102 # fitsection[2], fitsection[0], fitsection[1]
103 # , fitnextaddr, nextfixedaddr - fitnextaddr)
104 firstfixed = fixedsections[0][0]
106 # Find overall start position
110 for section in textsections + rodatasections + datasections:
111 size, align, name = section
112 if align > restalign:
114 restspace = alignpos(restspace, align) + size
115 restsections.append(section)
116 startrest = (firstfixed - restspace) / restalign * restalign
119 total = MAXPOS-firstfixed
120 slack = total - totalused
121 print ("Fixed space: 0x%x-0x%x total: %d slack: %d"
122 " Percent slack: %.1f%%" % (
123 firstfixed, MAXPOS, total, slack,
124 (float(slack) / total) * 100.0))
127 output = open(outname, 'wb')
130 code16_start = ABSOLUTE(.) ;
134 # Write regular sections
135 for section in restsections:
137 if rodatasections and name == rodatasections[0][2]:
138 output.write("code16_rodata = . ;\n")
139 output.write("*(%s)\n" % (name,))
141 # Write fixed sections
142 for addr, section, extrasections in fixedsections:
144 output.write(". = ( 0x%x - code16_start ) ;\n" % (addr,))
145 output.write("*(%s)\n" % (name,))
146 for extrasection in extrasections:
147 output.write("*(%s)\n" % (extrasection[2],))
151 code16_end = ABSOLUTE(.) ;
156 ######################################################################
157 # 32bit section outputting
158 ######################################################################
160 def outsections(file, sections, prefix):
162 for size, align, name in sections:
163 if name[:lp] == prefix:
164 file.write("*(%s)\n" % (name,))
166 def doLayout32(sections, outname):
167 output = open(outname, 'wb')
168 outsections(output, sections, '.text.')
169 output.write("code32_rodata = . ;\n")
170 outsections(output, sections, '.rodata')
171 outsections(output, sections, '.data.')
172 outsections(output, sections, '.bss.')
175 ######################################################################
176 # Section garbage collection
177 ######################################################################
179 def keepsection(name, pri, alt):
181 # Already kept - nothing to do.
184 relocs = pri[2].get(name)
187 # Keep all sections that this section points to
188 for symbol in relocs:
189 section = pri[1].get(symbol)
190 if section is not None and section[:9] != '.discard.':
191 keepsection(section, pri, alt)
193 # Not in primary sections - it may be a cross 16/32 reference
194 section = alt[1].get(symbol)
195 if section is not None:
196 keepsection(section, alt, pri)
198 def gc(info16, info32):
199 # pri = (sections, symbols, relocs, keep sections)
200 pri = (info16[0], info16[1], info16[2], [])
201 alt = (info32[0], info32[1], info32[2], [])
202 # Start by keeping sections that are globally visible.
203 for size, align, section in info16[0]:
204 if section[:11] == '.fixedaddr.' or '.export.' in section:
205 keepsection(section, pri, alt)
206 # Return sections found.
208 for info in info16[0]:
209 size, align, section = info
210 if section not in pri[3]:
211 # print "gc16", section
213 sections16.append(info)
215 for info in info32[0]:
216 size, align, section = info
217 if section not in alt[3]:
218 # print "gc32", section
220 sections32.append(info)
221 return sections16, sections32
224 ######################################################################
225 # Startup and input parsing
226 ######################################################################
228 # Read in output from objdump
229 def parseObjDump(file):
230 # sections = [(size, align, section), ...]
232 # symbols[symbol] = section
234 # relocs[section] = [symbol, ...]
238 for line in file.readlines():
240 if line == 'Sections:':
243 if line == 'SYMBOL TABLE:':
246 if line[:24] == 'RELOCATION RECORDS FOR [':
248 relocsection = line[24:-2]
251 if state == 'section':
253 idx, name, size, vma, lma, fileoff, align = line.split()
254 if align[:3] != '2**':
256 sections.append((int(size, 16), 2**int(align[3:]), name))
260 if state == 'symbol':
262 section, off, symbol = line[17:].split()
264 if '*' not in section:
265 symbols[symbol] = section
271 off, type, symbol = line.split()
273 relocs.setdefault(relocsection, []).append(symbol)
276 return sections, symbols, relocs
280 in16, in32, out16, out32 = sys.argv[1:]
282 infile16 = open(in16, 'rb')
283 infile32 = open(in32, 'rb')
285 info16 = parseObjDump(infile16)
286 info32 = parseObjDump(infile32)
288 sections16, sections32 = gc(info16, info32)
290 doLayout16(sections16, out16)
291 doLayout32(sections32, out32)
293 if __name__ == '__main__':