Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Dwarf5 as emitted by gcc-11.2 #69

Merged
merged 8 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ jobs:
gcc -g -o hello.exe hello.c &&
bin/${{env.BUILD_CONFIGURATION}}*/cv2pdb.exe hello.exe world.exe &&
ls -l hello* world*
ls -l hello* world* &&
curl -Lo cvdump.exe https://raw.githubusercontent.com/microsoft/microsoft-pdb/HEAD/cvdump/cvdump.exe &&
./cvdump.exe world.pdb >world.cvdump &&
grep '^S_PUB32: .*, Flags: 00000000, main$' world.cvdump
67 changes: 22 additions & 45 deletions src/PEImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,6 @@ PEImage::PEImage(const TCHAR* iname)
, hdr32(0)
, hdr64(0)
, fd(-1)
, debug_aranges(0)
, debug_pubnames(0)
, debug_pubtypes(0)
, debug_info(0), debug_info_length(0)
, debug_abbrev(0), debug_abbrev_length(0)
, debug_line(0), debug_line_length(0)
, debug_frame(0), debug_frame_length(0)
, debug_str(0)
, debug_loc(0), debug_loc_length(0)
, debug_ranges(0), debug_ranges_length(0)
, codeSegment(0)
, linesSegment(-1)
, reloc(0), reloc_length(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in #68 (review), I like to review PRs commit by commit, and if there is too much in one single commit, it is easy for bugs to hide. I did not find any bug in this, but I would not be surprised if I missed any because of the different goals of this commit: dropping debug_aranges and friends, refactoring PESections, adding new ones, dropping commented-out code, adding debugging, etc ;-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your concern. I was mainly aiming here for a testable chunk which shouldn't introduce functional changes.

I have a different philosophy about code review: it's really only worthwhile for architectural analysis and for suggesting ways to improve the readability or understandability of code. For bugs, however, tests are the gold standard. Especially for code like this where an off-by-one error or subtle spec misreading can lead to wildly different results. I think we've seen over the decades that the "thousand eyes make bugs shallow" idea doesn't really work in practice.

, nsec(0)
, nsym(0)
, symtable(0)
Expand Down Expand Up @@ -470,6 +457,15 @@ static DWORD sizeInImage(const IMAGE_SECTION_HEADER& sec)
return sec.SizeOfRawData < sec.Misc.VirtualSize ? sec.SizeOfRawData : sec.Misc.VirtualSize;
}

void PEImage::initSec(PESection& peSec, int secNo) const
{
auto &imgSec = sec[secNo];

peSec.length = sizeInImage(imgSec);
peSec.base = DPV<byte>(imgSec.PointerToRawData, peSec.length);
peSec.secNo = secNo;
}

void PEImage::initDWARFSegments()
{
for(int s = 0; s < nsec; s++)
Expand All @@ -480,49 +476,30 @@ void PEImage::initDWARFSegments()
int off = strtol(name + 1, 0, 10);
name = strtable + off;
}
if(strcmp(name, ".debug_aranges") == 0)
debug_aranges = DPV<char>(sec[s].PointerToRawData, sizeInImage(sec[s]));
if(strcmp(name, ".debug_pubnames") == 0)
debug_pubnames = DPV<char>(sec[s].PointerToRawData, sizeInImage(sec[s]));
if(strcmp(name, ".debug_pubtypes") == 0)
debug_pubtypes = DPV<char>(sec[s].PointerToRawData, sizeInImage(sec[s]));
if(strcmp(name, ".debug_info") == 0)
debug_info = DPV<char>(sec[s].PointerToRawData, debug_info_length = sizeInImage(sec[s]));
if(strcmp(name, ".debug_abbrev") == 0)
debug_abbrev = DPV<char>(sec[s].PointerToRawData, debug_abbrev_length = sizeInImage(sec[s]));
if(strcmp(name, ".debug_line") == 0)
debug_line = DPV<char>(sec[linesSegment = s].PointerToRawData, debug_line_length = sizeInImage(sec[s]));
if (strcmp(name, ".debug_line_str") == 0)
debug_line_str = DPV<char>(sec[s].PointerToRawData, debug_line_str_length = sizeInImage(sec[s]));
if(strcmp(name, ".debug_frame") == 0)
debug_frame = DPV<char>(sec[s].PointerToRawData, debug_frame_length = sizeInImage(sec[s]));
if(strcmp(name, ".debug_str") == 0)
debug_str = DPV<char>(sec[s].PointerToRawData, sizeInImage(sec[s]));
if(strcmp(name, ".debug_loc") == 0)
debug_loc = DPV<char>(sec[s].PointerToRawData, debug_loc_length = sizeInImage(sec[s]));
if(strcmp(name, ".debug_ranges") == 0)
debug_ranges = DPV<char>(sec[s].PointerToRawData, debug_ranges_length = sizeInImage(sec[s]));
if(strcmp(name, ".reloc") == 0)
reloc = DPV<char>(sec[s].PointerToRawData, reloc_length = sizeInImage(sec[s]));
if(strcmp(name, ".text") == 0)
codeSegment = s;

for (const SectionDescriptor *sec_desc : sec_descriptors) {
if (!strcmp(name, sec_desc->name)) {
PESection& peSec = this->*(sec_desc->pSec);
initSec(peSec, s);
}
}
}
}

bool PEImage::relocateDebugLineInfo(unsigned int img_base)
{
if(!reloc || !reloc_length)
if(!reloc.isPresent())
return true;

char* relocbase = reloc;
char* relocend = reloc + reloc_length;
byte* relocbase = reloc.startByte();
byte* relocend = reloc.endByte();
while(relocbase < relocend)
{
unsigned int virtadr = *(unsigned int *) relocbase;
unsigned int chksize = *(unsigned int *) (relocbase + 4);

char* p = RVA<char> (virtadr, 1);
if(p >= debug_line && p < debug_line + debug_line_length)
if(debug_line.isPtrInside(p))
{
for (unsigned int w = 8; w < chksize; w += 2)
{
Expand All @@ -536,7 +513,7 @@ bool PEImage::relocateDebugLineInfo(unsigned int img_base)
}
}
}
if(chksize == 0 || chksize >= reloc_length)
if(chksize == 0 || chksize >= reloc.length)
break;
relocbase += chksize;
}
Expand All @@ -545,7 +522,7 @@ bool PEImage::relocateDebugLineInfo(unsigned int img_base)

int PEImage::getRelocationInLineSegment(unsigned int offset) const
{
return getRelocationInSegment(linesSegment, offset);
return getRelocationInSegment(debug_line.secNo, offset);
}

int PEImage::getRelocationInSegment(int segment, unsigned int offset) const
Expand Down
108 changes: 92 additions & 16 deletions src/PEImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,81 @@
struct OMFDirHeader;
struct OMFDirEntry;

typedef unsigned char byte;

struct SymbolInfo
{
int seg;
unsigned long off;
bool dllimport;
};

struct PESection
{
byte* base;
unsigned long length;
unsigned int secNo;

PESection()
: base(0)
, length(0)
, secNo(0)
{
}

byte* byteAt(unsigned int off) const
{
return base + off;
}

byte* startByte() const
{
return byteAt(0);
}

byte* endByte() const
{
return byteAt(0) + length;
}

bool isPresent() const
{
return base && length;
}

bool isPtrInside(const void *p) const
{
auto pInt = (uintptr_t)p;
return (pInt >= (uintptr_t)base && pInt < (uintptr_t)base + length);
}

unsigned int sectOff(void *p) const
{
return (unsigned int)((uintptr_t)p - (uintptr_t)base);
}
};

// Define the list of interesting PE sections in one place so that we can
// generate definitions needed to populate our pointers and reference each
// section.

#define SECTION_LIST() \
EXPANDSEC(debug_addr) \
EXPANDSEC(debug_info) \
EXPANDSEC(debug_abbrev) \
EXPANDSEC(debug_line) \
EXPANDSEC(debug_line_str) \
EXPANDSEC(debug_frame) \
EXPANDSEC(debug_str) \
EXPANDSEC(debug_str_offsets) \
EXPANDSEC(debug_loc) \
EXPANDSEC(debug_loclists) \
EXPANDSEC(debug_ranges) \
EXPANDSEC(debug_rnglists) \
EXPANDSEC(reloc) \
EXPANDSEC(text)


#define IMGHDR(x) (hdr32 ? hdr32->x : hdr64->x)

class PEImage : public LastError
Expand Down Expand Up @@ -70,14 +138,15 @@ class PEImage : public LastError
bool save(const TCHAR* oname);

bool replaceDebugSection (const void* data, int datalen, bool initCV);
void initSec(PESection& peSec, int secNo) const;
bool initCVPtr(bool initDbgDir);
bool initDbgPtr(bool initDbgDir);
bool initDWARFPtr(bool initDbgDir);
bool initDWARFObject();
void initDWARFSegments();
bool relocateDebugLineInfo(unsigned int img_base);

bool hasDWARF() const { return debug_line != 0; }
bool hasDWARF() const { return debug_line.isPresent(); }
bool isX64() const { return x64; }
bool isDBG() const { return dbgfile; }

Expand Down Expand Up @@ -131,23 +200,30 @@ class PEImage : public LastError

public:
//dwarf
char* debug_aranges;
char* debug_pubnames;
char* debug_pubtypes;
char* debug_info; unsigned long debug_info_length;
char* debug_abbrev; unsigned long debug_abbrev_length;
char* debug_line; unsigned long debug_line_length;
char* debug_line_str; unsigned long debug_line_str_length;
char* debug_frame; unsigned long debug_frame_length;
char* debug_str;
char* debug_loc; unsigned long debug_loc_length;
char* debug_ranges; unsigned long debug_ranges_length;
char* reloc; unsigned long reloc_length;

int linesSegment;
int codeSegment;
#define EXPANDSEC(name) PESection name;
SECTION_LIST()
#undef EXPANDSEC

int cv_base;
};

struct SectionDescriptor {
const char *name;
PESection PEImage::* pSec;
};

#define EXPANDSEC(name) constexpr SectionDescriptor sec_desc_##name { "." #name, &PEImage::name };
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cv2pdb is also compiled with Visual D, see https://ci.appveyor.com/project/rainers/visuald. The CI still uses VS2017, I'm not sure how well this works with constexpr. Can it be avoided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested all the way back to VS2015 and this usage of constexpr is supported back there:
https://godbolt.org/z/349xjv44T

Do you still want me to remove it?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for verifying that it works in older VS, too. No need to change it then.

SECTION_LIST()
#undef EXPANDSEC

constexpr const SectionDescriptor *sec_descriptors[] =
{
#define EXPANDSEC(name) &sec_desc_##name,
SECTION_LIST()
#undef EXPANDSEC
};


#undef SECTION_LIST

#endif //__PEIMAGE_H__
20 changes: 17 additions & 3 deletions src/cv2pdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

static const int typePrefix = 4;

CV2PDB::CV2PDB(PEImage& image)
CV2PDB::CV2PDB(PEImage& image, DebugLevel debug_)
: img(image), pdb(0), dbi(0), tpi(0), ipi(0), libraries(0), rsds(0), rsdsLen(0), modules(0), globmod(0)
, segMap(0), segMapDesc(0), segFrame2Index(0), globalTypeHeader(0)
, globalTypes(0), cbGlobalTypes(0), allocGlobalTypes(0)
Expand All @@ -28,7 +28,7 @@ CV2PDB::CV2PDB(PEImage& image)
, srcLineStart(0), srcLineSections(0)
, pointerTypes(0)
, Dversion(2)
, debug(false)
, debug(debug_)
, classEnumType(0), ifaceEnumType(0), cppIfaceEnumType(0), structEnumType(0)
, classBaseType(0), ifaceBaseType(0), cppIfaceBaseType(0), structBaseType(0)
, emptyFieldListType(0)
Expand Down Expand Up @@ -149,7 +149,7 @@ bool CV2PDB::openPDB(const TCHAR* pdbname, const TCHAR* pdbref)

if (!initMsPdb ())
return setError("cannot load PDB helper DLL");
if (debug)
if (debug & DbgBasic)
{
extern HMODULE modMsPdb;
char modpath[260];
Expand Down Expand Up @@ -737,6 +737,9 @@ int CV2PDB::countNestedTypes(const codeview_reftype* fieldlist, int type)
int CV2PDB::addAggregate(codeview_type* dtype, bool clss, int n_element, int fieldlist, int property,
int derived, int vshape, int structlen, const char* name, const char* uniquename)
{
if (debug & DbgPdbTypes)
fprintf(stderr, "%s:%d: adding aggregate %s -> fieldlist:%d\n", __FUNCTION__, __LINE__, name, fieldlist);

dtype->struct_v2.id = clss ? (v3 ? LF_CLASS_V3 : LF_CLASS_V2) : (v3 ? LF_STRUCTURE_V3 : LF_STRUCTURE_V2);
dtype->struct_v2.n_element = n_element;
dtype->struct_v2.fieldlist = fieldlist;
Expand Down Expand Up @@ -771,6 +774,9 @@ int CV2PDB::addStruct(codeview_type* dtype, int n_element, int fieldlist, int pr
int CV2PDB::addEnum(codeview_type* dtype, int count, int fieldlist, int property,
int type, const char*name)
{
if (debug & DbgPdbTypes)
fprintf(stderr, "%s:%d: adding enum %s -> fieldlist:%d\n", __FUNCTION__, __LINE__, name, fieldlist);

dtype->enumeration_v2.id = (v3 ? LF_ENUM_V3 : LF_ENUM_V2);
dtype->enumeration_v2.count = count;
dtype->enumeration_v2.fieldlist = fieldlist;
Expand Down Expand Up @@ -2074,6 +2080,9 @@ int CV2PDB::appendTypedef(int type, const char* name, bool saveTranslation)
if(type == 0x78)
basetype = 0x75; // dchar type not understood by debugger, use uint instead

if (debug & DbgPdbTypes)
fprintf(stderr, "%s:%d: adding typedef %s -> %d\n", __FUNCTION__, __LINE__, name, type);

int typedefType;
if(useTypedefEnum)
{
Expand Down Expand Up @@ -2981,6 +2990,9 @@ bool CV2PDB::addPublics()
char symname[kMaxNameLen];
dsym2c((BYTE*)sym->data_v1.p_name.name, sym->data_v1.p_name.namelen, symname, sizeof(symname));
int type = translateType(sym->data_v1.symtype);
if (debug & DbgPdbSyms)
fprintf(stderr, "%s:%d: AddPublic2 %s\n", __FUNCTION__, __LINE__, (const char *)symname);

if (mod)
rc = mod->AddPublic2(symname, sym->data_v1.segment, sym->data_v1.offset, type);
else
Expand All @@ -2997,6 +3009,8 @@ bool CV2PDB::addPublics()

bool CV2PDB::initGlobalSymbols()
{
if (debug & DbgBasic)
fprintf(stderr, "%s:%d, countEntries: %d\n", __FUNCTION__, __LINE__, (int)countEntries);
for (int m = 0; m < countEntries; m++)
{
OMFDirEntry* entry = img.getCVEntry(m);
Expand Down
Loading