From 5b5ae51b22b57897dd61b9f8910e510757fa59c5 Mon Sep 17 00:00:00 2001 From: axis Date: Thu, 24 Jun 2010 14:07:49 +0200 Subject: Fixed several problems with the postlinker for Symbian (elf2e32). The fixes were too complicated for a profile and so a perl script was introduced. These are the issues that were addressed: - Made sure that we link correctly in all cases where there is a mismatch between the DEF file and the ELF file. Specifically, these two cases: - If elf2e32 detects a symbols in the ELF file that is marked as absent in the DEF file, an ordinal mapping will not be generated in the dso file. This means that binaries linking to the library will not find the symbol, even though it exists in the code. Since this is completely useless behavior, this was fixed to include the symbol regardless. - If the DEF file contains a symbol that is not present in the ELF file, an ordinal mapping will be created in the dso file regardless, which means applications can link to a symbol that does not exist. This is even worse than point one, since it is not detected until runtime, when the dynamic link will fail. Fixed by leaving the symbol out of the dso file if it does not exist in the ELF file. Both fixes require rerunning elf2e32 to fix the symbol mappings that were broken in the first run, but it only happens if at least one symbol is broken in the way described above. Also, freezing the DEF files will "save" the symbols, so that it does not occur after that. Note that this does not remove the ability to get the postlinker to tell you about mismatches. By doing "QMAKE_ELF2E32_FLAGS -= --unfrozen", the symbol mismatches will be considered errors instead of warnings, and they can be caught that way. - Code that was previously in the profile was moved to the perl script: - Detecting that there are errors in the postlink and return nonzero. elf2e32 does not do this on its own (at least not when using Wine). - Only overwriting the old dso file if the new one is different. This saves build time, since binaries that depend on the library do not trigger relink if the dso has not changed. These two points simplify the code, and they will also help the porting to Windows, since the old shell script tricks would not have worked there. RevBy: Trust me --- bin/elf2e32_qtwrapper | 145 ++++++++++++++++++++++++++ mkspecs/features/symbian/def_files.prf | 4 +- mkspecs/features/symbian/symbian_building.prf | 13 +-- 3 files changed, 151 insertions(+), 11 deletions(-) create mode 100755 bin/elf2e32_qtwrapper diff --git a/bin/elf2e32_qtwrapper b/bin/elf2e32_qtwrapper new file mode 100755 index 0000000..694d54a --- /dev/null +++ b/bin/elf2e32_qtwrapper @@ -0,0 +1,145 @@ +#!/usr/bin/perl -w + +# A script to get around some shortcomings in elf2e32, namely: +# - Returning 0 even when there are errors. +# - Excluding symbols from the dso file even when they are present in the ELF file. +# - Including symbols in the the dso file even when they are not present in the ELF file. +# - Overwriting the old dso file even when there are no changes (increases build time). + +use File::Copy; + +my @args = (); +my @definput; +my @defoutput; +my @dso; +my @tmpdso; +foreach (@ARGV) { + if (/^--definput/o) { + @definput = split('=', $_); + } elsif (/^--defoutput/o) { + @defoutput = split('=', $_); + } elsif (/^--dso/o) { + @dso = split('=', $_); + } elsif (/^--tmpdso/o) { + @tmpdso = split('=', $_); + $tmpdso[0] = "--dso"; + } else { + push(@args, $_); + } +} + +@definput = () if (!@definput || ! -e $definput[1]); + +if (@dso && !@tmpdso || !@dso && @tmpdso) { + print("--dso and --tmpdso must be used together.\n"); + exit 1; +} + +my $buildingLibrary = (@defoutput && @dso) ? 1 : 0; + +my $fixupFile = ""; +my $runCount = 0; +my $returnCode = 0; + +while (1) { + if (++$runCount > 2) { + print("Internal error in $0, link succeeded, but exports may be wrong.\n"); + last; + } + + my $elf2e32Pipe; + my $elf2e32Cmd = "elf2e32 @args" + . " " . join("=", @definput) + . " " . join("=", @defoutput) + . " " . join("=", @tmpdso); + open($elf2e32Pipe, "$elf2e32Cmd 2>&1 |") or die ("Could not run elf2e32"); + + my %fixupSymbols; + my $foundBrokenSymbols = 0; + my $errors = 0; + while (<$elf2e32Pipe>) { + print; + if (/Error:/io) { + $errors = 1; + } elsif (/symbol ([a-z0-9_]+) absent in the DEF file, but present in the ELF file/io) { + $fixupSymbols{$1} = 1; + $foundBrokenSymbols = 1; + } elsif (/[0-9]+ Frozen Export\(s\) missing from the ELF file/io) { + $foundBrokenSymbols = 1; + } + } + close($elf2e32Pipe); + + if ($errors) { + $returnCode = 1; + last; + } + + if ($buildingLibrary) { + my $tmpDefFile; + my $defFile; + open($defFile, "< $defoutput[1]") or die("Could not open $defoutput[1]"); + open($tmpDefFile, "> $defoutput[1].tmp") or die("Could not open $defoutput[1].tmp"); + $fixupFile = "$defoutput[1].tmp"; + while (<$defFile>) { + s/\r//; + s/\n//; + next if (/; NEW:/); + if (/([a-z0-9_]+) @/i) { + if (exists($fixupSymbols{$1})) { + s/ ABSENT//; + } elsif (s/; MISSING://) { + s/$/ ABSENT/; + } + } + print($tmpDefFile "$_\n"); + } + close($defFile); + close($tmpDefFile); + + $definput[1] = "$defoutput[1].tmp"; + + if (!$foundBrokenSymbols || $errors) { + last; + } + + print("Rerunning elf2e32 due to DEF file / ELF file mismatch\n"); + } else { + last; + } +}; + +if ($fixupFile) { + unlink($defoutput[1]); + move($fixupFile, $defoutput[1]); +} + +exit $returnCode if ($returnCode != 0); + +if ($buildingLibrary) { + my $differenceFound = 0; + + if (-e $dso[1]) { + my $dsoFile; + my $tmpdsoFile; + my $dsoBuf; + my $tmpdsoBuf; + open($dsoFile, "< $dso[1]") or die("Could not open $dso[1]"); + open($tmpdsoFile, "< $tmpdso[1]") or die("Could not open $tmpdso[1]"); + binmode($dsoFile); + binmode($tmpdsoFile); + while(read($dsoFile, $dsoBuf, 4096) && read($tmpdsoFile, $tmpdsoBuf, 4096)) { + if ($dsoBuf ne $tmpdsoBuf) { + $differenceFound = 1; + } + } + close($tmpdsoFile); + close($dsoFile); + } else { + $differenceFound = 1; + } + + if ($differenceFound) { + copy($tmpdso[1], $dso[1]); + } +} diff --git a/mkspecs/features/symbian/def_files.prf b/mkspecs/features/symbian/def_files.prf index 1b8e551..bae9d2d 100644 --- a/mkspecs/features/symbian/def_files.prf +++ b/mkspecs/features/symbian/def_files.prf @@ -56,7 +56,7 @@ symbian-abld|symbian-sbsv2 { } else { elf2e32FileToAdd = $$_PRO_FILE_PWD_/$$defFile } - QMAKE_ELF2E32_FLAGS += "`test -e $$elf2e32FileToAdd && echo --definput=$$elf2e32FileToAdd`" + QMAKE_ELF2E32_FLAGS += "--definput=$$elf2e32FileToAdd" symbianObjdir = $$OBJECTS_DIR isEmpty(symbianObjdir):symbianObjdir = . @@ -64,7 +64,7 @@ symbian-abld|symbian-sbsv2 { freeze_target.target = freeze freeze_target.depends = first # The perl part is to convert to unix line endings and remove comments, which the s60 tools refuse to do. - freeze_target.commands = perl -n -e \'next if (/; NEW/); s/\\r//g; if (/MISSING:(.*)/x) { print(\"\$\$1 ABSENT\\n\"); } else { print; }\' < $$symbianObjdir/$${TARGET}.def > $$elf2e32FileToAdd + freeze_target.commands = $$QMAKE_COPY $$symbianObjdir/$${TARGET}.def $$elf2e32FileToAdd QMAKE_EXTRA_TARGETS += freeze_target } else:contains(TEMPLATE, subdirs) { freeze_target.target = freeze diff --git a/mkspecs/features/symbian/symbian_building.prf b/mkspecs/features/symbian/symbian_building.prf index 5861bb6..bdab8d5 100644 --- a/mkspecs/features/symbian/symbian_building.prf +++ b/mkspecs/features/symbian/symbian_building.prf @@ -106,7 +106,7 @@ contains(TEMPLATE, lib):!contains(CONFIG, static):!contains(CONFIG, staticlib) { # The comparison of dso files is to avoid extra building of modules that depend on this dso, in # case it has not changed. QMAKE_POST_LINK = $$QMAKE_MOVE $$symbianDestdir/$${TARGET}.dll $$symbianDestdir/$${TARGET}.sym \ - && elf2e32 --version=$$decVersion \ + && elf2e32_qtwrapper --version=$$decVersion \ --sid=$$TARGET.SID \ --uid1=0x10000079 \ --uid2=$$TARGET.UID2 \ @@ -114,7 +114,8 @@ contains(TEMPLATE, lib):!contains(CONFIG, static):!contains(CONFIG, staticlib) { --targettype=DLL \ --elfinput=$${symbianDestdir}/$${TARGET}.sym \ --output=$${symbianDestdir}/$${TARGET}.dll \ - --dso=$$symbianObjdir/$${TARGET}.dso \ + --tmpdso=$${symbianObjdir}/$${TARGET}.dso \ + --dso=$${symbianDestdir}/$${TARGET}.dso \ --defoutput=$$symbianObjdir/$${TARGET}.def \ --linkas=$${TARGET}\\{$${hexVersion}\\}\\[$${intUid3}\\].dll \ --heap=$$join(TARGET.EPOCHEAPSIZE, ",") \ @@ -122,11 +123,6 @@ contains(TEMPLATE, lib):!contains(CONFIG, static):!contains(CONFIG, staticlib) { $$elf2e32_LIBPATH \ $$capability \ $$QMAKE_ELF2E32_FLAGS \ - | tee elf2e32.log && test `grep -c 'Error:' elf2e32.log` = 0 && rm elf2e32.log \ - && if ! diff -q $${symbianObjdir}/$${TARGET}.dso $${symbianDestdir}/$${TARGET}.dso \ - > /dev/null 2>&1; then \ - $$QMAKE_COPY $${symbianObjdir}/$${TARGET}.dso $${symbianDestdir}/$${TARGET}.dso; \ - fi \ $$QMAKE_POST_LINK QMAKE_DISTCLEAN += $${symbianDestdir}/$${TARGET}.sym QMAKE_DISTCLEAN += $${symbianDestdir}/$${TARGET}.dso @@ -159,7 +155,7 @@ contains(TEMPLATE, app):!contains(QMAKE_LINK, "^@.*") { } # the tee and grep at the end work around the issue that elf2e32 doesn't return non-null on error QMAKE_POST_LINK = $$QMAKE_MOVE $$symbianDestdir/$${TARGET} $$symbianDestdir/$${TARGET}.sym \ - && elf2e32 --version $$decVersion \ + && elf2e32_qtwrapper --version $$decVersion \ --sid=$$TARGET.SID \ --uid1=0x1000007a \ --uid2=$$TARGET.UID2 \ @@ -173,7 +169,6 @@ contains(TEMPLATE, app):!contains(QMAKE_LINK, "^@.*") { $$elf2e32_LIBPATH \ $$capability \ $$QMAKE_ELF2E32_FLAGS \ - | tee elf2e32.log && test `grep -c 'Error:' elf2e32.log` = 0 && rm elf2e32.log \ && ln "$${symbianDestdir}/$${TARGET}.exe" "$${symbianDestdir}/$$TARGET" \ $$QMAKE_POST_LINK QMAKE_DISTCLEAN += $${symbianDestdir}/$${TARGET}.sym -- cgit v0.12