1214571Sdim#!/usr/bin/perl
2214571Sdim# -*- perl -*-
3214571Sdim#
4214571Sdim# Toshiba MeP Media Engine Relocation Generator
5214571Sdim# Copyright (C) 2001, 2007 Free Software Foundation, Inc.
6214571Sdim# This file is part of BFD.
7214571Sdim# Originally written by DJ Delorie <dj@redhat.com>
8214571Sdim#
9214571Sdim# This program is free software; you can redistribute it and/or modify
10214571Sdim# it under the terms of the GNU General Public License as published by
11214571Sdim# the Free Software Foundation; either version 2 of the License, or
12214571Sdim# (at your option) any later version.
13214571Sdim#
14214571Sdim# This program is distributed in the hope that it will be useful,
15214571Sdim# but WITHOUT ANY WARRANTY; without even the implied warranty of
16214571Sdim# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17214571Sdim# GNU General Public License for more details.
18214571Sdim#
19214571Sdim# You should have received a copy of the GNU General Public License
20214571Sdim# along with this program; if not, write to the Free Software
21214571Sdim# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
22214571Sdim
23214571Sdim
24214571Sdim# Usage: Run this anywhere inside your source tree.  It will read
25214571Sdim# include/elf/mep.h and scan the comments therein.  It will renumber
26214571Sdim# the relocs to be sequential (this is needed so that bfd/elf32-mep.h
27214571Sdim# works) if needed.  It will then update the reloc list in bfd/reloc.c
28214571Sdim# and the howto, mapping, and apply routines in bfd/elf32-mep.c.  You
29214571Sdim# can then regenerate bfd-in2.h and check everything in.
30214571Sdim
31214571Sdim# FIXME: After the relocation list is finalized, change this to
32214571Sdim# *verify* the reloc list, rather than resequence it.
33214571Sdim
34214571Sdimwhile (! -f "include/elf/mep.h" && ! -f "bfd/reloc.c") {
35214571Sdim    chdir "..";
36214571Sdim    $pwd = `pwd`;
37214571Sdim    if ($pwd !~ m@/.*/@) {
38214571Sdim	print STDERR "Cannot find include/elf/mep.h or bfd/reloc.h\n";
39214571Sdim	exit 1;
40214571Sdim    }
41214571Sdim}
42214571Sdim$pwd = `pwd`;
43214571Sdimprint "srctop is $pwd";
44214571Sdim
45214571Sdimprintf "Reading include/elf/mep.h ...\n";
46214571Sdimopen(MEPH, "include/elf/mep.h");
47214571Sdimopen(MEPHO, "> include/elf/mep.h.new") || die("mep.h.new create: $!");
48214571Sdim$val = 0;
49214571Sdimwhile (<MEPH>) {
50214571Sdim    if (($pre,$rel,$rest) = /(.*RELOC_NUMBER \()([^,]+), *\d+(.*)/) {
51214571Sdim	$rest =~ s/[\r\n]+$//;
52214571Sdim	print (MEPHO "$pre$rel, $val$rest\n") || die("mep.h.new write: $!");
53214571Sdim	$val ++;
54214571Sdim	$rel =~ s/R_MEP_//;
55214571Sdim	push(@relocs, $rel);
56214571Sdim
57214571Sdim	$rest =~ s@.*/\* @@;
58214571Sdim	($pattern, $sign, $attrs) = $rest =~ m@(.*) ([US]) (.*)\*/@;
59214571Sdim	$pattern =~ s/ //g;
60214571Sdim	push(@pattern, $pattern);
61214571Sdim	push(@sign, $sign);
62214571Sdim	push(@attrs, $attrs);
63214571Sdim
64214571Sdim	printf "%4d $rel p=`$pattern' s=`$sign' a=`$attrs'\n", $#pattern;
65214571Sdim
66214571Sdim    } else {
67214571Sdim	print(MEPHO) || die("mep.h.new write: $!");
68214571Sdim    }
69214571Sdim}
70214571Sdimclose(MEPH);
71214571Sdimclose(MEPHO) || die("mep.h.new close: $!");
72214571Sdim
73214571Sdim&swapfile("include/elf/mep.h");
74214571Sdim
75214571Sdimredo_file ("bfd/reloc.c",
76214571Sdim	   "",
77214571Sdim	   "ENUMDOC\n  Toshiba Media Processor Relocations.\n\nCOMMENT\n",
78214571Sdim	   "ENUM\n  BFD_RELOC_MEP_%s\n",
79214571Sdim	   "");
80214571Sdim
81214571Sdim$autogen = "    /* This section generated from bfd/mep-relocs.pl from include/elf/mep.h.  */\n";
82214571Sdim
83214571Sdimredo_file ("bfd/elf32-mep.c",
84214571Sdim	   "MEPRELOC:HOWTO",
85214571Sdim	   $autogen,
86214571Sdim	   "MEPRELOC:END",
87214571Sdim	   "",
88214571Sdim	   "&emit_howto();",
89214571Sdim	   "MEPRELOC:MAP",
90214571Sdim	   $autogen,
91214571Sdim	   "MEPRELOC:END",
92214571Sdim	   "",
93214571Sdim	   "    MAP(%s);\n",
94214571Sdim	   "MEPRELOC:APPLY",
95214571Sdim	   $autogen,
96214571Sdim	   "MEPRELOC:END",
97214571Sdim	   "",
98214571Sdim	   "&emit_apply();",
99214571Sdim	   );
100214571Sdim
101214571Sdimsub mask2shifts {
102214571Sdim    my ($mask) = @_;
103214571Sdim    my ($bits, $left, $right, $ci, $c, $cv);
104214571Sdim    $bits = 0;
105214571Sdim    $left = 0;
106214571Sdim    $right = 32;
107214571Sdim    for ($ci=0; $ci<length($mask); $ci++) {
108214571Sdim	$c = substr($mask, $ci, 1);
109214571Sdim	$left++;
110214571Sdim	next if $c eq '-';
111214571Sdim	$left = 0;
112214571Sdim	$cv = ord($c) - ord('0');
113214571Sdim	$cv -= ord('a') - ord('9') - 1 if $cv > 9;
114214571Sdim	$right = $cv unless $right < $cv;
115214571Sdim	$bits = $cv+1 unless $bits > $cv+1;
116214571Sdim    }
117214571Sdim    $mask =~ tr/-/1/c;
118214571Sdim    $mask =~ tr/-/0/;
119214571Sdim    ($rmask = $mask) =~ tr/01/10/;
120214571Sdim    $mask = unpack("H*", pack("B*", $mask));
121214571Sdim    $rmask = unpack("H*", pack("B*", $rmask));
122214571Sdim    return ($bits, $left, $right, $mask, $rmask);
123214571Sdim}
124214571Sdim
125214571Sdimsub emit_howto {
126214571Sdim    for ($i=2; $i<=$#relocs; $i++) {
127214571Sdim	$mask = $pattern[$i];
128214571Sdim
129214571Sdim	if (length($mask) == 8)     { $bytesize = 0; }
130214571Sdim	elsif (length($mask) == 16) { $bytesize = 1; }
131214571Sdim	elsif (length($mask) == 32) { $bytesize = 2; }
132214571Sdim
133214571Sdim	($bits, $left, $right, $mask) = mask2shifts ($mask);
134214571Sdim	$bits[$i] = $bits;
135214571Sdim	$pcrel = 0;
136214571Sdim	$pcrel = 1 if $attrs[$i] =~ /pc-rel/i;
137214571Sdim	$overflow = $sign[$i];
138214571Sdim	$overflow = 'N' if $attrs[$i] =~ /no-overflow/;
139214571Sdim
140214571Sdim	$c = "$relocs[$i],";
141214571Sdim	printf(NEW "  MEPREL (R_MEP_%-10s%d,%3d,%2d,%2d,%2d,%2s, 0x%s),\n",
142214571Sdim	       $c, $bytesize, $bits, $left, $right, $pcrel, $overflow, $mask);
143214571Sdim    }
144214571Sdim}
145214571Sdim
146214571Sdimsub emit_apply {
147214571Sdim    for ($i=2; $i<=$#relocs; $i++) {
148214571Sdim	$v = "u";
149214571Sdim	$v = "s" if $sign[$i] =~ /S/;
150214571Sdim	if (length($pattern[$i]) == 8) {
151214571Sdim	    $e = ''; # no endian swap for bytes
152214571Sdim	} elsif ($pattern[$i] =~ /-/ || length($pattern[$i]) == 16) {
153214571Sdim	    $e = '^e2'; # endian swap - 2byte words only
154214571Sdim	} else {
155214571Sdim	    $e = '^e4' # endian swap for data
156214571Sdim	}
157214571Sdim	print NEW "    case R_MEP_$relocs[$i]: /* $pattern[$i] */\n";
158214571Sdim	if ($attrs[$i] =~ /tp-rel/i) {
159214571Sdim	    print NEW "      $v -= mep_tpoff_base(rel->r_offset);\n";
160214571Sdim	}
161214571Sdim	if ($attrs[$i] =~ /gp-rel/i) {
162214571Sdim	    print NEW "      $v -= mep_sdaoff_base(rel->r_offset);\n";
163214571Sdim	}
164214571Sdim	if ($attrs[$i] !~ /no-overflow/ && $bits[$i] < 32) {
165214571Sdim	    if ($v eq "u") {
166214571Sdim		$max = (1 << $bits[$i]) - 1;
167214571Sdim		print NEW "      if (u > $max) r = bfd_reloc_overflow;\n";
168214571Sdim	    } else {
169214571Sdim		$min = -(1 << ($bits[$i]-1));
170214571Sdim		$max = (1 << ($bits[$i]-1)) - 1;
171214571Sdim		print NEW "      if ($min > s || s > $max) r = bfd_reloc_overflow;\n";
172214571Sdim	    }
173214571Sdim	}
174214571Sdim	for ($b=0; $b<length($pattern[$i]); $b += 8) {
175214571Sdim	    $mask = substr($pattern[$i], $b, 8);
176214571Sdim	    ($bits, $left, $right, $mask, $rmask) = mask2shifts ($mask);
177214571Sdim	    if ($left > $right) { $left -= $right; $right = 0; }
178214571Sdim	    else { $right -= $left; $left = 0; }
179214571Sdim
180214571Sdim	    if ($mask ne "00") {
181214571Sdim		$bb = $b / 8;
182214571Sdim		print NEW "      byte[$bb$e] = ";
183214571Sdim		print NEW "(byte[$bb$e] & 0x$rmask) | " if $rmask ne "00";
184214571Sdim		if ($left) {
185214571Sdim		    print NEW "(($v << $left) & 0x$mask)";
186214571Sdim		} elsif ($right) {
187214571Sdim		    print NEW "(($v >> $right) & 0x$mask)";
188214571Sdim		} else {
189214571Sdim		    print NEW "($v & 0x$mask)";
190214571Sdim		}
191214571Sdim		print NEW ";\n";
192214571Sdim	    }
193214571Sdim	}
194214571Sdim	print NEW "      break;\n";
195214571Sdim    }
196214571Sdim}
197214571Sdim
198214571Sdim
199214571Sdim#-----------------------------------------------------------------------------
200214571Sdim
201214571Sdimsub redo_file {
202214571Sdim    my ($file, @control) = @_;
203214571Sdim    open(OLD, $file);
204214571Sdim    open(NEW, "> $file.new") || die("$file.new create: $!");
205214571Sdim
206214571Sdim    print "Scanning file $file ...\n";
207214571Sdim
208214571Sdim    while (1) {
209214571Sdim	$start = shift @control;
210214571Sdim	$prefix = shift @control;
211214571Sdim	$end = shift @control;
212214571Sdim	$suffix = shift @control;
213214571Sdim	$pattern = shift @control;
214214571Sdim
215214571Sdim	if (!$start) {
216214571Sdim	    print NEW while <OLD>;
217214571Sdim	    last;
218214571Sdim	}
219214571Sdim
220214571Sdim	print "  looking for $start\n";
221214571Sdim	while (<OLD>) {
222214571Sdim	    print NEW;
223214571Sdim	    last if /\Q$start\E/;
224214571Sdim	}
225214571Sdim	print "can't find $start\n" unless $_;
226214571Sdim	last unless $_;
227214571Sdim
228214571Sdim	print NEW $prefix;
229214571Sdim	if ($pattern =~ /^\&/) {
230214571Sdim	    eval $pattern;
231214571Sdim	    die("$pattern: $@") if $@;
232214571Sdim	} else {
233214571Sdim	    for $i (2..$#relocs) {
234214571Sdim		printf (NEW "$pattern", $relocs[$i]) || die("$file.new write: $!");
235214571Sdim		$pattern =~ s/^ENUM\n/ENUMX\n/;
236214571Sdim	    }
237214571Sdim	}
238214571Sdim	print NEW $suffix;
239214571Sdim	while (<OLD>) {
240214571Sdim	    last if /\Q$end\E/;
241214571Sdim	}
242214571Sdim	print NEW;
243214571Sdim    }
244214571Sdim
245214571Sdim    close(OLD);
246214571Sdim    close(NEW) || die("$file.new close: $!");
247214571Sdim    &swapfile($file);
248214571Sdim}
249214571Sdim
250214571Sdim#-----------------------------------------------------------------------------
251214571Sdim
252214571Sdimsub swapfile {
253214571Sdim    my ($f) = @_;
254214571Sdim    if ( ! -f "$f.save") {
255214571Sdim	system "cp $f $f.save";
256214571Sdim    }
257214571Sdim    open(ORIG, $f);
258214571Sdim    open(NEW, "$f.new");
259214571Sdim    while (<ORIG>) {
260214571Sdim	$n = <NEW>;
261214571Sdim	if ($n ne $_) {
262214571Sdim	    close(ORIG);
263214571Sdim	    close(NEW);
264214571Sdim	    print "  Updating $f\n";
265214571Sdim	    rename "$f", "$f.old";
266214571Sdim	    rename "$f.new", "$f";
267214571Sdim	    return;
268214571Sdim	}
269214571Sdim    }
270214571Sdim    close(ORIG);
271214571Sdim    close(NEW);
272214571Sdim    print "  No change to $f\n";
273214571Sdim    unlink "$f.new";
274214571Sdim}
275