Created
January 29, 2012 18:28
-
-
Save stbuehler/1700019 to your computer and use it in GitHub Desktop.
preware build.git: build stub libs for libs from PalmSDK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
# Creates source code to build a stub library for a shared library | |
# Takes the path to a shared library, creates sources in the local | |
# directory (does NOT override anything) and prints Makefile lines | |
# to stdout. | |
# needs "objdump" in the path | |
# Copyright (C) 2012 Stefan Bühler <stbuehler@web.de> | |
# This program is free software; you can redistribute it and/or | |
# modify it under the terms of the GNU General Public License version 2 | |
# as published by the Free Software Foundation | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# You should have received a copy of the GNU General Public License | |
# along with this program; if not, write to the Free Software | |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
use strict; | |
use Fcntl qw(:DEFAULT :mode); | |
use IO::Handle; | |
use File::Basename; | |
my $fh; | |
my $lib = $ARGV[0]; | |
if ($lib !~ /\.so[0-9.]*/) { | |
print STDERR "'$lib' doesn't look like a shared libary filename\n"; | |
exit(1); | |
} | |
{ | |
my @st = lstat($lib); | |
unless (scalar(@st)) { | |
print STDERR "couldn't open shared library '$lib'\n"; | |
exit(1); | |
} | |
if (S_ISLNK($st[2])) { | |
# symlink | |
my $target = readlink($lib); | |
print "$lib: $target\n\tln -s $target $lib\n\n"; | |
exit(0); | |
} | |
unless (S_ISREG($st[2])) { | |
print STDERR "couldn't open shared library: not a regular file: '$lib'\n"; | |
exit(1); | |
} | |
} | |
my %symVersions; | |
my @localSymbols; | |
# needed for versioned symbols, so we have "unique" internal names | |
my $MAGIC = 'voi2ees'; | |
my $counter = 1; | |
my $sourceText; | |
sub warnSymbol { | |
my ($msg) = shift; | |
print "Warning '$msg': couldn't parse symbol '@_'\n"; | |
} | |
sub versionName { | |
my ($name) = @_; | |
if ($name =~ m/^\((.*)\)$/) { | |
return $1 unless wantarray; | |
return ($1, 0); | |
} else { | |
return $name unless wantarray; | |
return ($name, 1); | |
} | |
} | |
# creates version if it didn't exist | |
sub getVersion { | |
my ($name) = @_; | |
$name = versionName($name); | |
$symVersions{$name} = { base => undef, symbols => [ ] } unless defined $symVersions{$name}; | |
return $symVersions{$name}; | |
} | |
sub isVersion { | |
my ($name) = @_; | |
$name = versionName($name); | |
return (defined $symVersions{$name}); | |
} | |
sub addVersionInheritance { | |
my ($base, $version) = @_; | |
my $v = getVersion($version); | |
$v->{'base'} = $base; | |
getVersion($base); | |
return; | |
} | |
sub addVersionSymbol { | |
my ($version, $symbol) = @_; | |
my $v = getVersion($version); | |
push @{$v->{'symbols'}}, $symbol; | |
return ($symbol, '') if $version eq 'Base'; | |
# setup version mapping | |
my $priv_sym = $symbol.'__'.$MAGIC.'_'.$counter; | |
++$counter; | |
my $default; | |
($version, $default) = versionName($version); | |
push @localSymbols, $priv_sym; | |
return ($priv_sym, 'asm(".symver '.$priv_sym.','.$symbol . ($default ? '@@' : '@') . $version . '");' . "\n"); | |
} | |
# normal C function with 'g DF' flags | |
# we don't care about address and size | |
sub addFunction { | |
my ($section, $version, $name) = @_; | |
my $symText; | |
($name, $symText) = addVersionSymbol($version, $name); | |
my $attrs = ''; | |
$attrs .= ' __attribute__ ((section ("'.$section.'")))' if $section ne '.text'; | |
$sourceText .= "void$attrs $name(void) { }\n".$symText; | |
return; | |
} | |
# flags 'g DO', section '.rodata'. need size, don't care about address | |
sub addReadonlyObject { | |
my ($size, $version, $name) = @_; | |
my $symText; | |
($name, $symText) = addVersionSymbol($version, $name); | |
if ($size > 0) { | |
$sourceText .= sprintf("const unsigned char %s[0x%x] = { 0 };\n", $name, $size).$symText; | |
} else { | |
$sourceText .= sprintf("const unsigned char %s[0] = { };\n", $name).$symText; | |
} | |
} | |
# flags 'g DO', section '.data'. need size, don't care about address | |
sub addInitializedObject { | |
my ($size, $version, $name) = @_; | |
my $symText; | |
($name, $symText) = addVersionSymbol($version, $name); | |
if ($size > 0) { | |
$sourceText .= sprintf("unsigned char %s[0x%x] = { 1 };\n", $name, $size).$symText; | |
} else { | |
# cheat: size 0 with initialization not possible. probably not needed anyway :) | |
$sourceText .= sprintf("unsigned char %s[1] = { 1 };\n", $name).$symText; | |
} | |
} | |
# flags 'g DO', section '.bss'. need size, don't care about address | |
sub addUninitializedObject { | |
my ($size, $version, $name) = @_; | |
my $symText; | |
($name, $symText) = addVersionSymbol($version, $name); | |
$sourceText .= sprintf("unsigned char %s[0x%x];\n", $name, $size).$symText; | |
} | |
# flags 'g DO', section '.data.rel.ro'. need size, don't care about address | |
sub addDynRelReadonlyObject { | |
my ($size, $version, $name) = @_; | |
my $symText; | |
($name, $symText) = addVersionSymbol($version, $name); | |
# create self reference, with -fPIC this results in .data.rel.ro | |
$sourceText .= sprintf("void * const %s[0x%x/sizeof(void*)] = { (void*) &%s };\n", $name, $size, $name).$symText; | |
} | |
sub parseSymbol { | |
my ($value, $flags, $section, $size_or_align, $version, $name) = @_; | |
my @flags = split //, $flags; | |
my $ivalue = hex($value); | |
my $isize_or_align = hex($size_or_align); | |
return if ($flags[0] eq 'l' || $flags[0] eq ' '); # skip local and "neither local/global" symbols | |
return warnSymbol('global+local symbol', @_) if ($flags[0] eq '!'); | |
# now $flags[0] is either g (global) or u (unique global) | |
return if ($section eq '.init' && $name eq '_init'); | |
return if ($section eq '.fini' && $name eq '_fini'); | |
return if ($flags[6] eq ' '); # drop non-"object/filename/function" | |
return warnSymbol('symbol refers to a filename', @_) if $flags[6] eq 'f'; | |
return addFunction($section, $version, $name) if $flags eq 'g DF'; | |
return addReadonlyObject($isize_or_align, $version, $name) if ($flags eq 'g DO' && $section eq '.rodata'); | |
return addInitializedObject($isize_or_align, $version, $name) if ($flags eq 'g DO' && $section eq '.data'); | |
return addUninitializedObject($isize_or_align, $version, $name) if ($flags eq 'g DO' && $section eq '.bss'); | |
return addDynRelReadonlyObject($isize_or_align, $version, $name) if ($flags eq 'g DO' && $section eq '.data.rel.ro'); | |
if ($version eq $name || defined $symVersions{$name}) { | |
return warnSymbol('invalid version symbol', @_) if ($ivalue != 0 || $flags ne 'g DO' || $section ne '*ABS*' || $isize_or_align != 0); | |
addVersionInheritance($version, $name); | |
return; | |
} | |
print "Found Symbol: '@_'\n"; | |
} | |
sub generateMapEntry { | |
my ($version) = @_; | |
my $v = getVersion($version); | |
return '' if $v->{'generated'}; | |
$v->{'generated'} = 1; | |
return '' if ($version eq 'Base'); # don't generate "Base" | |
my $txt = ''; | |
$txt .= generateMapEntry($v->{'base'}) if defined $v->{'base'}; | |
$txt .= $version . " {\n"; | |
$txt .= "\tglobal:\n\t\t" . join(";\n\t\t", @{$v->{'symbols'}}) . ";\n" if scalar(@{$v->{'symbols'}}) > 0; | |
if (scalar(@localSymbols) > 0) { | |
$txt .= "\tlocal:\n\t\t" . join(";\n\t\t", @localSymbols) . ";\n"; | |
@localSymbols = (); | |
} | |
$txt .= '}'; | |
$txt .= ' '.$v->{'base'} if defined $v->{'base'} && $v->{'base'} ne $version; | |
$txt .= ";\n"; | |
return $txt; | |
} | |
# only run once! | |
sub generateMap { | |
my @versions = keys %symVersions; | |
my $txt = ''; | |
if (0 == scalar(@versions) || (1 == scalar(@versions) && $versions[0] eq 'Base')) { | |
$txt .= "{\n"; | |
$txt .= "\tglobal: *;\n"; | |
if (scalar(@localSymbols) > 0) { | |
$txt .= "\tlocal:\n\t\t" . join(";\n\t\t", @localSymbols) . ";\n"; | |
@localSymbols = (); | |
} | |
$txt .= "}\n;"; | |
} else { | |
foreach my $v (@versions) { | |
$txt .= generateMapEntry($v); | |
} | |
} | |
return $txt; | |
} | |
open($fh, '-|', "objdump -T -p $lib"); | |
my $mode = 0; | |
my $soName; | |
while (<$fh>) { | |
chomp; | |
if (0 == $mode) { | |
if (/^DYNAMIC SYMBOL TABLE:$/) { | |
$mode = 1; | |
} elsif (/^Dynamic Section:$/) { | |
$mode = 2; | |
} | |
} elsif (1 == $mode) { | |
if (/^$/) { | |
$mode = 0; | |
} elsif (/^([0-9a-f]+)[ \t]([lgu! ][w ][C ][W ][Ii ][dD ][FfO ])[ \t](\S+)[ \t]+([0-9a-f]+)[ \t]+(\S*)[ \t]+(\S+)$/) { | |
parseSymbol($1, $2, $3, $4, $5, $6); | |
} else { | |
print STDERR "Cannot parse symbol table line: '", $_, "'\n"; | |
} | |
} elsif (2 == $mode) { | |
if (/^$/) { | |
$mode = 0; | |
} elsif (/^[ \t]*SONAME[ \t]+(\S+)$/) { | |
$soName = "-Wl,-soname,$1"; | |
} | |
} | |
} | |
$lib = basename($lib); | |
my $basename = $lib; | |
my $srcFname = $lib . '_stub.c'; | |
my $mapFname = $lib . '_stub.map'; | |
print "$lib: $srcFname $mapFname\n"; | |
print "\t\$(CC) \$(CFLAGS) -shared $soName $srcFname -o $lib -fPIC -Wl,--no-undefined -Wl,--version-script=$mapFname\n\n"; | |
my ($SRCFILE, $MAPFILE); | |
sysopen($SRCFILE, $srcFname, O_WRONLY|O_CREAT|O_EXCL) or die "couldn't open '$srcFname': $!"; | |
print $SRCFILE $sourceText; | |
close $SRCFILE; | |
sysopen($MAPFILE, $mapFname, O_WRONLY|O_CREAT|O_EXCL) or die "couldn't open '$srcFname': $!"; | |
print $MAPFILE generateMap(); | |
close $MAPFILE; | |
exit(0); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NAME = PDK | |
TITLE = PDK | |
APP_ID = org.webosinternals.toolchain.${NAME} | |
VERSION = 1.0-1 | |
MAINTAINER = WebOS Internals <support@webos-internals.org> | |
.PHONY: package | |
package: build/.built-${VERSION} | |
.PHONY: stage | |
stage: build/.staged-${VERSION} | |
build/.staged-${VERSION}: build/armv7.staged-${VERSION} build/armv6.staged-${VERSION} build/i686.staged-${VERSION} | |
touch $@ | |
build/%.staged-${VERSION} : build/%.built-${VERSION} | |
mkdir -p ../../staging/$*/usr/include | |
cp -rp build/$*/usr/include/* ../../staging/$*/usr/include/ | |
mkdir -p ../../staging/$*/usr/lib | |
cp -rp build/$*/usr/lib/* ../../staging/$*/usr/lib/ | |
touch $@ | |
include ../../support/cross-compile.mk | |
.PHONY: unpack | |
unpack: build/.unpacked-${VERSION} | |
build/.unpacked-${VERSION}: | |
rm -rf build | |
mkdir -p build/armv6 build/armv7 build/i686 | |
cp -rip src build/src | |
touch $@ | |
.PHONY: build | |
build: build/.built-${VERSION} | |
build/.built-${VERSION}: build/armv7.built-${VERSION} build/armv6.built-${VERSION} build/i686.built-${VERSION} | |
touch $@ | |
build/%.built-${VERSION}: build/.unpacked-${VERSION} | |
rm -rf build/$* | |
${MAKE} -C build/src CC=${CROSS_COMPILE_$*}gcc DESTDIR=../$*/ clobber all install | |
touch $@ | |
clobber:: | |
rm -rf build | |
update-source: | |
rm -rf src | |
mkdir -p src | |
cp -ar /opt/PalmPDK/include src/ | |
# exclude runtime provided libs: libc-* libc.* libcrypt.so* libdl.* libgcc_s.so* libm-* libm.* libpthread-* libpthread.* librt.so* libstdc++.so* libutil.so* | |
@echo ".PHONY: libs all clobber install" >> src/Makefile | |
@echo "all: libs" >> src/Makefile | |
@echo >> src/Makefile | |
@(cd src; \ | |
LIBS=""; \ | |
for lib in $$(find /opt/PalmPDK/device/lib/ -type f -name '*.so*' \ | |
-a ! -name libc-* -a ! -name libc.* -a ! -name libcrypt.so* -a ! -name libdl.* -a ! -name libgcc_s.so* -a ! -name libm-* \ | |
-a ! -name libm.* -a ! -name libpthread-* -a ! -name libpthread.* -a ! -name librt.so* -a ! -name libstdc++.so* -a ! -name libutil.so* | sort); do \ | |
LIBS="$$LIBS $$(basename $$lib)"; \ | |
../create-stub-lib $$lib >> Makefile; \ | |
done; \ | |
echo "libs: $$LIBS" >> Makefile; \ | |
echo >> Makefile; \ | |
echo "clobber:" >> Makefile; \ | |
echo " rm -f $$LIBS" >> Makefile; \ | |
echo >> Makefile; \ | |
echo "install: libs" >> Makefile; \ | |
echo ' mkdir -p $$(DESTDIR)/usr/lib' >> Makefile; \ | |
echo ' cp -ar include $$(DESTDIR)/usr/' >> Makefile; \ | |
echo " cp -a $$LIBS "'$$(DESTDIR)/usr/lib/' >> Makefile; \ | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment