This is an archive of the discontinued LLVM Phabricator instance.

[llvm-objdump][COFF] Skip empty entries when dumping the export table
ClosedPublic

Authored by aganea on May 1 2023, 12:39 PM.

Details

Summary

Ditto. dumpbin /exports and binutils objdump do skip empty entries as well. This patch is to simplify an upcoming change in LLD.

Diff Detail

Event Timeline

aganea created this revision.May 1 2023, 12:39 PM
Herald added a reviewer: MaskRay. · View Herald Transcript
Herald added a project: Restricted Project. · View Herald Transcript
Herald added a subscriber: hiraditya. · View Herald Transcript
aganea requested review of this revision.May 1 2023, 12:39 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 1 2023, 12:39 PM
mstorsjo added inline comments.May 1 2023, 12:46 PM
lld/test/COFF/export32.test
89

This change doesn't seem consistent and/or directly explainable by the change itself?

llvm/tools/llvm-objdump/COFFDump.cpp
570

This part of the change also seems a bit unrelated to the main change. If you want it bundled here, make sure to at least mention/explain it in the commit message.

hans added a comment.May 2 2023, 1:54 AM

Can you share what future change this will simplify? The current output seems natural in a way.

Can you share what future change this will simplify? The current output seems natural in a way.

I think it might be D149611 - which is marked as a child revision, but doesn't seem to be made including the changes from this patch.

Currently, for these cases we do have empty stub entries in the export tables, but with D149611 we'll also be creating export tables that start from a different ordinal. I think it might be good to visually see the difference between those two cases?

I don't think we should skip empty exports, especially not by checking only the RVA, because it looks like it is theoretically possible to have RVA = 0 but a non-empty name for an export. Even entries with null RVA and null name seems worth keeping as they do take up space in the export table.

I don't think we need to follow exactly the behaviour of dumpbin. Can you check what Binutils objdump does just for comparison?

aganea marked 2 inline comments as done.May 2 2023, 10:47 AM

@hans @alvinhochun The rationale is to simply remove noise from the output when there are sparse ordinals in the table. I'm not quite sure how useful is to keeps those empty entries, since they will be skipped over by the kernel anyway. The check in COFFObjectFile.cpp, L1567 checks if the RVA is in the output sections. If it's outside the sections, that entry isn't exporting anything, and there will be no dynamic linking at runtime.

I don't think we need to follow exactly the behaviour of dumpbin. Can you check what Binutils objdump does just for comparison?

objdump skips over empty values, as this patch does (using a test added by D149611):

C:\git\llvm-project>objdump --version
GNU objdump (Binutils for MinGW-W64 x86_64, built by Brecht Sanders) 2.36.1
Copyright (C) 2021 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.

C:\git\llvm-project>objdump -p "C:\git\llvm-project\stage1_msvc_debug\tools\lld\test\COFF\Output\ordinals-override.test.tmp.dll"
...
The Export Tables (interpreted .rdata section contents)

Export Flags                    0
Time/Date stamp                 0
Major/Minor                     0/0
Name                            0000000000016848 ordinals-override.test.tmp.dll
Ordinal Base                    122
Number in:
        Export Address Table            00002698
        [Name Pointer/Ordinal] Table    00000003
Table Addresses
        Export Address Table            0000000000016867
        Name Pointer Table              00000000000202c7
        Ordinal Table                   00000000000202d3

Export Address Table -- Ordinal Base 122
        [   0] +base[ 122] 1010 Export RVA
        [9878] +base[10000] 1000 Export RVA
        [9879] +base[10001] 1020 Export RVA

[Ordinal/Name Pointer] Table
        [   0] ?bar@@YAXXZ
        [9878] ?foo@@YAXXZ
        [9879] baz
lld/test/COFF/export32.test
89

The previous pattern was wrong. The RVA 0 was actually matching the first character of 0x1010.

llvm/tools/llvm-objdump/COFFDump.cpp
570

I was planning to commit it separately as a NFC patch, is that ok with you?

mstorsjo added inline comments.May 2 2023, 11:19 AM
lld/test/COFF/export32.test
89

Ok, that makes sense. Please mention and explain that bit in the commit message then!

llvm/tools/llvm-objdump/COFFDump.cpp
570

Yes, that sounds good, I’d rather have that as a separate commit.

aganea marked 3 inline comments as done.May 2 2023, 11:56 AM
aganea added inline comments.
lld/test/COFF/export32.test
89

I could separate that in a commit as well, since it's not relevant to this patch.

jhenderson added inline comments.May 3 2023, 12:23 AM
llvm/lib/Object/COFFObjectFile.cpp
1568–1570 ↗(On Diff #518530)

Drive-by comments as I noticed these when my herald rule triggered:

I believe static_cast is generally preferred to C-style casts.

Why consumeError rather than not return E? I guess it's because the interesting bit is whether or not getRvaPtr fails, but is there a risk it will fail for some other reason (I haven't looked at the underlying code at all, so there might not be)? Also, would it be better for this method to return Expected<bool> rather than pass in an input parameter for the result?

hans added a comment.May 3 2023, 2:02 AM

@hans @alvinhochun The rationale is to simply remove noise from the output when there are sparse ordinals in the table.

Fair enough. If sparse tables are a thing and other tools do it too, I suppose that makes sense.

@hans @alvinhochun The rationale is to simply remove noise from the output when there are sparse ordinals in the table.

Fair enough. If sparse tables are a thing and other tools do it too, I suppose that makes sense.

As long as the tool does print what the actual initial value was (which iirc it does?) I guess it makes sense.

@hans @alvinhochun The rationale is to simply remove noise from the output when there are sparse ordinals in the table. I'm not quite sure how useful is to keeps those empty entries, since they will be skipped over by the kernel anyway. [...]

I don't think we need to follow exactly the behaviour of dumpbin. Can you check what Binutils objdump does just for comparison?

objdump skips over empty values, as this patch does (using a test added by D149611):

Fair enough. Hiding completely empty entries may not be so bad then, given that two other tools do the same

The check in COFFObjectFile.cpp, L1567 checks if the RVA is in the output sections. If it's outside the sections, that entry isn't exporting anything, and there will be no dynamic linking at runtime.

Still, I don't quite agree with hiding any entries with non-zero RVA or non-empty names, even if they may not be linked at runtime. My opinion is that if an export entry has either, then it is likely they have been deliberately there for a reason or possibly created in error, so they should still be shown in the output for reference. (I think you can get such symbols using -export:zeroRvaExport=__ImageBase.)

So, I think the condition for skipping should only be RVA == 0 && Name.empty(). I would also consider doing the checks directly in the for loop without adding the ExportDirectoryEntryRef::isEmpty function. Just move move I->getSymbolNameearlier so you can check the RVA and the name before printing the entry.

lld/test/COFF/export.test
16–20

I'm thinking, since you are removing the empty entries in this patch, perhaps it would be a good idea to add checks for Ordinal base: 1 here also? This way the change to the ordinal base in the follow-up patch will be clearer.

aganea updated this revision to Diff 519167.May 3 2023, 10:53 AM
aganea marked 4 inline comments as done.
aganea edited the summary of this revision. (Show Details)

As suggested.

@hans @alvinhochun The rationale is to simply remove noise from the output when there are sparse ordinals in the table. I'm not quite sure how useful is to keeps those empty entries, since they will be skipped over by the kernel anyway. [...]

I don't think we need to follow exactly the behaviour of dumpbin. Can you check what Binutils objdump does just for comparison?

objdump skips over empty values, as this patch does (using a test added by D149611):

Fair enough. Hiding completely empty entries may not be so bad then, given that two other tools do the same

The check in COFFObjectFile.cpp, L1567 checks if the RVA is in the output sections. If it's outside the sections, that entry isn't exporting anything, and there will be no dynamic linking at runtime.

Still, I don't quite agree with hiding any entries with non-zero RVA or non-empty names, even if they may not be linked at runtime. My opinion is that if an export entry has either, then it is likely they have been deliberately there for a reason or possibly created in error, so they should still be shown in the output for reference. (I think you can get such symbols using -export:zeroRvaExport=__ImageBase.)

So, I think the condition for skipping should only be RVA == 0 && Name.empty(). I would also consider doing the checks directly in the for loop without adding the ExportDirectoryEntryRef::isEmpty function. Just move move I->getSymbolNameearlier so you can check the RVA and the name before printing the entry.

I changed as you said. Although binutils had just RVA == 0 for the past 24 years at least (I couldn't trace back to the source of the initial commit for that line): https://github.com/gittup/binutils/blame/gittup/bfd/peXXigen.c#L1524

lld/test/COFF/export.test
16–20

I did it in the other patch, but I could add it here earlier.

llvm/lib/Object/COFFObjectFile.cpp
1568–1570 ↗(On Diff #518530)

I ended up removing this code, see other comment.

alvinhochun accepted this revision.May 4 2023, 7:54 AM

Thanks, LGTM.

This revision is now accepted and ready to land.May 4 2023, 7:54 AM