Jump to content
Mefistotelis

Debug symbols for lvrt.dll

Recommended Posts

Labview exists for decades. It had thousands of releases.

When was the last time NI left debug symbols by mistake?

 

How to recognize a release with debug symbols left:

- Nowadays Visual Studio keeps the symbols in a PDB file - so probably lvrt.pdb, but it might also have different name - depends on whether the DLL is renamed after build.

- Previously, up to around year 2000, debug symbols were stored in the EXE/DLL - so lvrt.dll file size would be extraordinarily large, circa 2x-3x the normal size

- Most compilers can also generate MAP file - it doesn't store as much as full debug symbols do, but has all global variables and functions named - which is still way more than nothing

 

Share this post


Link to post
Share on other sites
On 11/21/2019 at 2:28 AM, Mefistotelis said:

Labview exists for decades. It had thousands of releases.

Thousends of releases? I kind of doubt it. Leaving away LabVIEW prior to the multiplatform version (2.2.x and earlier which only were Macintosh) there have been 2.5, 3.0, 3.1, 4.0, 5.0, 5.1, 6.0, 7.0, 7.1, 8.0, 8.2, 8.5, 8.6, 2009, 2010, 2011, 20012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 releases so far. Of each of them there was usually 1 and rarely two maintenance releases, and of each maintenance release between 1 to 8 bug fix releases. This does probably only amount to about 100 releases in total and maybe another 50 for beta releases of these versions (a beta has usually 2 to 4 intermediate releases although that tends to be no more than 2 in the last 10 years or so).

Quote

When was the last time NI left debug symbols by mistake?

How to recognize a release with debug symbols left:

- Nowadays Visual Studio keeps the symbols in a PDB file - so probably lvrt.pdb, but it might also have different name - depends on whether the DLL is renamed after build.

- Previously, up to around year 2000, debug symbols were stored in the EXE/DLL - so lvrt.dll file size would be extraordinarily large, circa 2x-3x the normal size

- Most compilers can also generate MAP file - it doesn't store as much as full debug symbols do, but has all global variables and functions named - which is still way more than nothing

I'm not aware of any LabVIEW release that had the debug symbols exposed. PDPs were used even in Microsoft Visual C 6.0, the first version that was probably used by NI to create a released LabVIEW version  (NI only switched to use Microsoft C for the standard 32-bit builds for Windows NT, the Windows 3.1 versions of LabVIEW were created using Watcom C 10.x which was the only compiler able to create full 32-bit executables that could run on 16-bit Windows 3.1 through the built-in DOS extender runtime).

Microsoft makes this anyhow pretty hard to happen by accident as such DLL/EXE files would normally link to the debug version of the Microsoft C Runtime library and you can't install that legally on a computer without installing the entire Microsoft C Compiler contained in the Visual Studio software. There is definitely no downloadable installer for the debug version of the C runtime engine.

The only early leak I'm aware of was that the original LabVIEW 2.5 prerelease contained a huge extcode.h file in the cintools directory that showed much more than the officially documented LabVIEW manager functions. About half of it was still pretty much unusable as you needed other functions that were not exposed in there to make use of some of the features, and a lot of those functions were removed from the exported functions around LabVIEW 4.0 and 5.0 as they were considered obsolete or undesirable to pollute the exported symbols list, but it did contain a few interesting functions that are still exported from the LabVIEW kernel but not declared in the current extcode.h file. They fixed that extcode.h bug before the official release of LabVIEW 3.0, which was the first non-beta version of LabVIEW running on other computers than Macintosh. (2.5 was basically a beta release called prerelease version to have something to show for NI Week 1992 that runs on Windows 3.1, and there was a 2.5.1 and I believe 2.5.2 bug fix release of it in 1993).

Also lvrt.dll is a development that only got introduced around LabVIEW 6.0. As this was released in 2000 it most likely used at least Microsoft Visual Studio C++ 6.0;

Before that the application builder was concatenating the generated runtime LLB to a stub executable that contained the entire LabVIEW runtime engine. That was a pretty neat feature as it created a single file executable, but as LabVIEW was extended and more and more functionality implemented in external files and DLLs that get linked dynamically, this was pretty much unmaintainable in the long run and the entire runtime engine was externalized. 

Edited by Rolf Kalbermatter
  • Like 1

Share this post


Link to post
Share on other sites

Thanks for all the info!

 

On 11/22/2019 at 12:53 PM, Rolf Kalbermatter said:

Thousends of releases? I kind of doubt it.

Searching for various NI things on the Internet, I found quite a lot of packages which included the LV RT. Various toolsets, drivers, and labview-based solutions. Though most of these use standard release of the RT installer.

Unfortunately I have no pre-release versions. And this is where I expect higher chance of having some debug info left.

I've seen indications that VI Logger 1.1 had a pre-release RT in it, but I could only find the fixed 1.1.1.

 

As of now, I couldn't find the LV 2.5 as well. I found LV 6.0 on one of my old CDs, that's the oldest version I have.

Share this post


Link to post
Share on other sites

LabVIEW 2.5 was distributed on 3.5" floppy disks.

220px-Floppy_disc.jpg.407329eaef3674b27f283ce078a01e9d.jpg

About 4 or 5 or so of these in 1.44MB high density formatted. I ditched them several years ago as it is pretty hard to find a drive nowadays to read them.

 Later releases were substantially more floppy disk. I don't think CD-ROM was an option before LabVIEW 6.0.

Edited by Rolf Kalbermatter

Share this post


Link to post
Share on other sites

I got a 3.5 inch floppy drive with USB interface - they were very cheap at some pint.

Anyway, I browsed through various releases available in obscure places of the web. There's A LOT of things created by NI which either use the standard LV runtime environment, or use parts of the LV code.

I downloaded > 100GB of various files when I decided it's time to stop. There was probably way more to find, but I have to limit my time.

Findings:

- Files from Windows platform are the worst port to reverse engineer, mostly because Windows libraries (.DLL) define exported routines explicitly.

- UNIX base releases (solaris,linux) are the best to debug - compiled for popular CPU architectures, and shared libs (.SO) have all routines exported - meaning their names are retained

- For some obscure platforms, I found static libraries (.a/.lib) files included in the release. These have the most info on source code retained, but not the whole code was in that form. Not the part I need anyway.

- The best version to focus on seem to be LV6.0 - the code is a lot less bloated than in newer releases, and the core of the tool didn't seem to have changed much since that release

(though I didn't found much LV5.0 releases - I can tell 4.0 is too old and does not have many blocks which exist in modern VI files, for 5.0 - I'm not sure)

 

Here's the loading of LVSR block in LabView 6.0. Unfortunately, I still don't have struct definitions. But at least I know how the structs are named, thanks to C++ name mangling.

void RToVI(SAVERECORD **sr, INSTRUMENT **instr)
{
  char fld10a;
  char fld10b;
  INSTRUMENT *dInstr;
  int state;

  (*instr)->sr_field4 = (((*sr)->field_4 & 0xFF0000) >> 8) | ((unsigned int)(*sr)->field_4 >> 24) | (((*sr)->field_4 & 0xFF00) << 8) | ((*sr)->field_4 << 24);
  (*instr)->sr_field8 = (((*sr)->field_8 & 0xFF0000) >> 8) | ((unsigned int)(*sr)->field_8 >> 24) | (((*sr)->field_8 & 0xFF00) << 8) | ((*sr)->field_8 << 24);
  (*instr)->sr_fieldC = (((*sr)->field_C & 0xFF0000) >> 8) | ((unsigned int)(*sr)->field_C >> 24) | (((*sr)->field_C & 0xFF00) << 8) | ((*sr)->field_C << 24);
  (*instr)->sr_field12 = _byteswap_ushort((*sr)->field_12);
  (*instr)->instrState = (((*sr)->instrState & 0xFF0000) >> 8) | ((unsigned int)(*sr)->instrState >> 24) | (((*sr)->instrState & 0xFF00) << 8) | ((*sr)->instrState << 24);
  (*instr)->sr_field14 = _byteswap_ushort((*sr)->field_14);
  (*instr)->sr_field16 = _byteswap_ushort((*sr)->field_16);
  (*instr)->sr_field20 = _byteswap_ushort((*sr)->field_20);
  (*instr)->sr_field22 = _byteswap_ushort((*sr)->field_22);
  (*instr)->version = (((*sr)->version & 0xFF0000) >> 8) | ((unsigned int)(*sr)->version >> 24) | (((*sr)->version & 0xFF00) << 8) | ((*sr)->version << 24);
  (*instr)->sr_field24 = (((*sr)->field_24 & 0xFF0000) >> 8) | ((unsigned int)(*sr)->field_24 >> 24) | (((*sr)->field_24 & 0xFF00) << 8) | ((*sr)->field_24 << 24);
  (*instr)->sr_field1C = (((*sr)->field_1C & 0xFF0000) >> 8) | ((unsigned int)(*sr)->field_1C >> 24) | (((*sr)->field_1C & 0xFF00) << 8) | ((*sr)->field_1C << 24);
  qmemcpy(&(*instr)->sr_field34_md5, (*sr)->field34_md6, 0x10u);
  qmemcpy(&(*instr)->sr_field28, &(*sr)->field_28, 0xCu);
  RevBL(&(*instr)->sr_field28);
  RevBL(&(*instr)->field_2C);
  RevBL(&(*instr)->field_30);
  fld10a = (*sr)->field_10;
  fld10b = HIBYTE((*sr)->field_10);
  (*instr)->is_sr_field10_flag0200 = HIBYTE((*sr)->field_10) & 2;
  (*instr)->is_sr_field10_flag0100 = fld10b & 1;
  (*instr)->is_sr_field10_flag0400 = fld10b & 4;
  dInstr = *instr;
  if ( (*instr)->sr_field4 & 0x20000 )
  {
    state = dInstr->instrState;
    if ( state & 0x200 )
    {
      dInstr->instrState = state & 0xFFFFFDFF;
      DBPrintf("Fixing instrState, remove viDebugCapable from subroutine VI");
    }
  }
}

 

Edited by Mefistotelis

Share this post


Link to post
Share on other sites
13 hours ago, Mefistotelis said:

Here's the loading of LVSR block in LabView 6.0. Unfortunately, I still don't have struct definitions. But at least I know how the structs are named, thanks to C++ name mangling.


void RToVI(SAVERECORD **sr, INSTRUMENT **instr)
{
  char fld10a;
  char fld10b;
  INSTRUMENT *dInstr;
  int state;

  (*instr)->sr_field4 = (((*sr)->field_4 & 0xFF0000) >> 8) | ((unsigned int)(*sr)->field_4 >> 24) | (((*sr)->field_4 & 0xFF00) << 8) | ((*sr)->field_4 << 24);
  ......
}

 

(((*sr)->field_4 & 0xFF0000) >> 8 | ((unsigned int)(*sr)->field_4 >> 24) | (((*sr)->field_4 & 0xFF00) << 8 | ((*sr)->field_4 << 24);

 

This is simply the BigEndian to LittleEndian swapping. All numeric values in the VI data structures are stored as Big Endian, since they need to be independent of

the CPU architecture. On loading into memory on LittleEndian platforms (currently all but the VxWorks platform for PowerPC Real-time platforms, but in the past

MacOS Classic (68k and PPC), Mac OS X for PPC, Sun Sparc and I believe HP PA RISC were all BigEndian versions of LabVIEW) they have to be swapped.

 

Basically this is a pretty unexciting routine. It just copies the data structure from the memory stream on disk into a real data structure in memory and accounts

for endian swapping on relevant platforms and massages some flags into a different format.

The in memory version of the INSTRUMENT data structure basically changed with every single LabVIEW version considerably, even with minor changes between minor

LabVIEW versions, so trying to access the fields at runtime in that structure is a very version dependent adventure.

Edited by Rolf Kalbermatter

Share this post


Link to post
Share on other sites
5 hours ago, Rolf Kalbermatter said:

Basically this is a pretty unexciting routine. It just copies the data structure from the memory stream on disk into a real data structure in memory and accounts

for endian swapping on relevant platforms and massages some flags into a different format.

You are completely right with the assessment of what this function does. But I can't agree it is unexciting:

- It provides us with full information on how to divide the LVSR block into single values, and it even names one of the values.

- It gives us offsets within INSTRUMENT where each value is stored, so that we can cross-reference it with other functions and check what they do

(as you see, I already named the items within INSTRUMENT to "sr_field*" so that I can easily search ehere each of these is used further.

So actually, almost everything about LVSR which is in my code, comes from this single function:

https://github.com/mefistotelis/pylabview/blob/master/LVblock.py#L100

4 hours ago, Cloedu72 said:

If you are interested in older versions, I can provide you a link to LV3.1.1:
ftp://ftp.ni.com/support/labview/updates/windows/win3x/31-to-311

Thanks! I actually downloaded that whole FTP already; and yes, this is where I got LV3.1.1 from. There are 2 binaries - Win16 and WinNT; the WinNT one is better for analysis. But, as I previously stated - before LV6.0, the blocks within VI files were a bit different - so I prefer to work on more recent code.

As an example, in LV5.0 there was LVIN block instead of the LVSR discussed above.

 

Edited by Mefistotelis

Share this post


Link to post
Share on other sites
3 hours ago, Mefistotelis said:

it gives us offsets within INSTRUMENT where each value is stored, so that we can cross-reference it

A good example I just found:

void InitButtonsHidden(INSTRUMENT **a1)
{
  (*a1)->sr_field14 = gButtonsHidden[(*a1)->sr_field22];
}

Now we can rename one field to 'buttonsHidden', and can assume another field contains enum value representing either state or type.

 

Edited by Mefistotelis

Share this post


Link to post
Share on other sites
On 11/28/2019 at 12:36 AM, Mefistotelis said:

A good example I just found:


void InitButtonsHidden(INSTRUMENT **a1)
{
  (*a1)->sr_field14 = gButtonsHidden[(*a1)->sr_field22];
}

Now we can rename one field to 'buttonsHidden', and can assume another field contains enum value representing either state or type.

The word hidden in there makes it look very exciting but the reality is that it is simply the toolbar buttons that are made invisible in the VI Properties. Apparently there is an enum that describes all possible permutations with individual enum labels and that enum gets also saved to disk but for performance reasons they decided to rather have a bitmask field to use at runtime. So this "routine" converts from the enum to the bitfield. There should most likely be another routine that does the reverse, most likely by running through the gButtonsHidden global array and comparing sr_field14 with its values and storing the found index in sr_field22, or more likely that routine is directly called in the SaveVI routine to generate the right enum value to store in the SAVERECORD version.

Why so complicated you may ask? Well the backsaving feature makes all kinds of complications necessary in order to be able to store VI files in older formats. 

Share this post


Link to post
Share on other sites
2 hours ago, Rolf Kalbermatter said:

Apparently there is an enum that describes all possible permutations with individual enum labels and that enum gets also saved to disk but for performance reasons they decided to rather have a bitmask field to use at runtime.

Nope. that's not what I found.

You're right with ButtonsHidden field being an integer which bits correspond to something (I assume visibility of some buttons), but sr_field22 turned out to be something else - the type of VI. This type is actually part of LV documentation:

https://zone.ni.com/reference/en-XX/help/371361R-01/lvprop/vi_vi_typ/

I already did proper renaming in my code.

 

There seem to be many properties which I could find in code based on the documentation (click "VI Properties" in the link above to see them). Below, they are listed with "property ID" of each - will check whether this can provide me with some more field names:

https://labviewwiki.org/wiki/VI_class

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.