* [FREELDR] Mark noreturn functions
* [FREELDR] Compile hw debugging support code only in debug builds
- Make BREAKPOINT() portable
* [FREELDR] Consolidate identical names into a single string
* [FREELDR] Use intrinsics for string I/O operations on x86 and x64
Stop them being pulled in from a static minihal library
* [MINIHAL] Exclude unnecessary portio dependency
This removes a dependency of UI code to boot options code by getting rid of a call to DisplayBootTimeOptions() from within the menu code.
Instead this is now handled by MainBootMenuKeyPressFilter.
URLs are getting old. We have to
update URLs for documentation
purpose.
JIRA issue: CORE-19963
- Refresh old URLs.
- Add " (DEAD_LINK)" labels
to dead links.
- Use MS Learn links rather
than MSDN ones.
- Some dead links revived by
Web Archive.
- Don't change Wine Tests
and Wine Sync.
- Don't change 3rd party libraries.
- Don't append "redirected" labels.
PeLdrCheckForLoadedDll():
- Use a for-loop to iterate over the linked list.
- Adjust few comments.
- Use SAL2 annotations.
PeLdrpCompareDllName():
- Make its input paramters const.
- Use SAL2 annotations.
Use the PE import table's OriginalFirstThunk array when scanning and
resolving imports during DLL binding.
It points to an array of pointer-sized IMAGE_THUNK_DATA structures
which describe the functions being imported. On the other hand, the
FirstThunk points to an array of pointers, whose initial values are
a copy of those pointed to by OriginalFirstThunk, but are replaced
by the actual function pointers determined at runtime, when a DLL
is loaded (see PeLdrpBindImportName() function).
If we were to use the FirstThunk array to find again later the imports
by name or ordinal, we would fail because these are replaced by the
addresses of the corresponding functions.
This fixes loading kdcom.dll from Windows XP x64 with FreeLDR when
testing on ReactOS x64.
```diff
(freeldr\freeldr\lib\peloader.c:498) trace: PeLdrpScanImportAddressTable() ---- Calling PeLdrpBindImportName() in a loop
(freeldr\freeldr\lib\peloader.c:501) trace: *** ThunkName->u1.AddressOfData = 00000000000070F0
(freeldr\freeldr\lib\peloader.c:502) trace: *** ThunkData->u1.AddressOfData = 0000000000573780
(freeldr\freeldr\lib\peloader.c:209) trace: !!! ExportDirectory->NumberOfNames 1504
-(freeldr\freeldr\lib\peloader.c:210) trace: !!! ImportHint 0 - ExportName 'CcCanIWrite' - ImportDataName ''
+(freeldr\freeldr\lib\peloader.c:210) trace: !!! ImportHint 282 - ExportName 'HalPrivateDispatchTable' - ImportDataName 'HalPrivateDispatchTable'
....
-(freeldr\freeldr\lib\peloader.c:268) err: Did not find export ''!
-(freeldr\freeldr\lib\peloader.c:709) err: PeLdrpScanImportAddressTable() failed: ImportName = 'ntoskrnl.exe', DirectoryPath = 'multi(0)disk(0)rdisk(0)partition(2)\ReactOS\system32\'
```
('-': lines before the fix; '+': lines after the fix)
Code has been adapted based from the following functions:
ntdll/ldr/ldrpe.c!LdrpSnapThunk() and LdrpSnapIAT()
ntoskrnl/mm/ARM3/sysldr.c!MiSnapThunk() and MiResolveImageReferences()
References:
https://devblogs.microsoft.com/oldnewthing/20231129-00/?p=109077https://devblogs.microsoft.com/oldnewthing/20231130-00/?p=109084https://stackoverflow.com/questions/42413937/why-pe-need-original-first-thunkoft
This allows to load an image as freeldr extension code.
TODO:
- Add global bootloader DTE list
- Add wrapper function that also processes imports
- Use this for scsiport
- The original code was just incrementing the reference counts (RefCounts)
of the device objects or the device/file handles, without decrementing
them when closing the handles. This is now fixed.
Notice the following:
* When opening a file on a device (disk), the device's (and its
handle's) RefCount is incremented, and the file handle's RefCount
is incremented as well.
* When closing a file, the file handle's RefCount is decremented
(and the file closed if the RefCount reaches zero), and the file's
parent device handle is also closed, recursively.
This has the effect of decrementing the parent device handle's
RefCount, and the device's own RefCount is decremented as well.
IMPORTANT NOTE: The usefulness of handle-level RefCount is still
under question, and might be (consistently) removed in the future.
- Fix opening a device (disk) in direct access, when this device is
already opened. Indeed, we previously allowed direct access only if
the device was opened as such for the very first time (its RefCount
= 0 originally); no filesystem mounting was attempted as well.
Then for any later open-operations on this device (while keeping an
already-opened handle to it), filesystem access was assumed.
Thus, this problem would show up in two ways:
* Either the device is first opened for direct access, this succeeded
and no filesystem was mounted. Then, for any other open-operations,
the filesystem was NOT mounted, and opening files on it would fail.
Direct accesses would succeed but would create an unnecessary second
file handle.
* Or, the device is first opened for file-access: a filesystem was
mounted and file opening would succeed. Any other file opening
operation would succeed as well (if the file exists). But, a direct
access open-operation would fail, because now any open-operations on
the device would be assumed to be a file opening.
This is now correctly fixed. If direct-open is requested, just do it.
If this is a file opening, we open the device, then try to mount a
filesystem on it (if not already done), then we try to open the file.
If file opening fails, derereference the device.
- Pass the file path to the filesystem-specific Open() functions without
truncating the leading path separator, if any. This has to be handled
by the filesystem routines themselves.
Those haven't been groomed anymore for more than 10 years. We had many thousands of
different freeldr builds with different behavior and bugs each, but nobody ever
did have the slightest motivation to update those hardcoded
FREELOADER_MAJOR_VERSION, FREELOADER_MINOR_VERSION, FREELOADER_PATCH_VERSION
from ver.h. And that is logical, because touching other modules will change the behavior
of freeldr as well, so it is absolutely impossible to groom anything like that correctly.
Instead we should simply do what we started to do in PR7383, which will at least give
some information (the actual sources it was built from) instead of some misleading voodoo-version.
This might slightly shrink the size of freeldr as well, but I was too lazy to measure by how much.
- `ArcOpen()`: flatten the registered-device search for-loop.
Limit it to just the device search, and exit early.
The rest of the initialization is now done outside the loop.
- The `DeviceName` string pointer may have been allocated from
the heap, for path normalization (see `NormalizeArcDeviceName()`).
This pointer is then only used in the device search loop and not
nused anymore afterwards. The old code didn't free this pointer
and memory could leak. This is now fixed easily, thanks to the
loop flattening.
- Rename `DEVICE` member `Prefix` to `DeviceName`; SAL-annotate
`FsRegisterDevice()`.
- Replace a lot of `MAX_FDS` by `_countof(FileData)`;
- The duplicated FileId validation logic is wrapped with
the `IS_VALID_FILEID()` macro.
- When returning an invalid FileId value on purpose, use
`INVALID_FILE_ID` instead of `MAX_FDS` (that could vary
if the file handle table gets extended).
And replace the `(ULONG)-1` also used for that purpose
by `INVALID_FILE_ID`.
- Add missing DeviceId invalidation:
* when failing to open a file handle in `ArcOpen()`;
* when registering a new device in `FsRegisterDevice()`.
There, having DeviceId always set to zero would tell
the code that the corresponding device's file ID is
the first one in the table, which is a BUG. (Many devices
would have the same file ID...)
- In addition: massage a bit some indicial for-loops.
This allows to make the code better extendable: adding a new FS
mount routine into the table, instead of duplicating also a whole
`if (!FileFuncTable) ...` check.
Later, this table will be made dynamic, so that new filesystems could be
dynamically registered at runtime, and a filesystem could be forced to
be mounted by the user (using a specific syntax).
----
Thanks to the following testers!
- Dmitry Borisov (@disean) for testing on NEC PC-98 emulator;
- Justin Miller (@DarkFire01) for testing on UEFI platform;
- Stanislav Motylkov (@binarymaster) for testing on Xbox emulator
(xemu), both livecd and bootcd.
----
"SectorsPerTrack" is for the legacy Cylinders/Heads/Sectors(PerTrack)
scheme.
- On BIOS-based PCs, INT 13h can return (for LBA-only drives) an invalid
geometry, like: C/H/S = (-1)/(-1)/(-1). This is also what happens in
our hwide.c driver (see IdentifyDevice() for ATAPI devices):
db419efbf2/boot/freeldr/freeldr/arch/drivers/hwide.c (L918-L928)
as well as on VirtualBox for CD-ROMs:
https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Devices/PC/BIOS/disk.c#L155
- Therefore, we cannot reliably calculate a valid total number of sectors
by multiplying the Cylinders*Heads*SectorsPerTrack values. In addition,
such a multiplication could overflow a 32-bit ULONG.
Thus, a separate ULONGLONG Sectors member is required to hold such a
value, that is retrieved differently. For example for ATAPI devices,
our hwide.c driver does return a valid TotalSectors value, even though
CHS values are invalid. Other platforms, like UEFI, just work using
logical block addressing (LBA) values (see EFI_BLOCK_IO_MEDIA).
- uefidisk.c : Per the spec, EFI_BLOCK_IO_MEDIA::LastBlock contains
"The last LBA on the device. [...] For ATA devices, this is reported
in IDENTIFY DEVICE data words 60-61 (i.e., Total number of user
addressable logical sectors) _minus one_.
For SCSI devices, this is reported in the READ CAPACITY parameter
data 'Returned Logical Block Address field' _minus one_."
In other words, LastBlock is a zero-based LBA index quantity. The
corresponding total number of valid "sectors"/blocks of the device
is therefore, (LastBlock + 1).
- Cleanup some old disabled code.