how to get goroutine id with ebpf - go

I use cilium ebpf pakage to write a ebpf program for getting the goroutine id.
but failed. my uprobe.c like this :
I think the key problem is that golang struct g trans to goroutine.h is wrong. can anyone help?
uprobe.c
SEC("uprobe/runtime.newproc1")
int uprobe_runtime_newproc1(struct pt_regs *ctx) {
u32 key = 2;
u64 initval = 1, *valp;
valp = bpf_map_lookup_elem(&uprobe_map, &key);
if (!valp) {
bpf_map_update_elem(&uprobe_map, &key, &initval, BPF_ANY);
return 0;
}
__sync_fetch_and_add(valp, 1);
struct g* goroutine_struct = (void *)PT_REGS_PARM4(ctx);
// retrieve output parameter
s64 goid = 0;
bpf_probe_read(&goid, sizeof(goid), &goroutine_struct->goid);
bpf_printk("bpf_printk bpf_probe_read goroutine_struct->goid: %lld", goid);
struct g gs;
bpf_probe_read(&gs, sizeof(gs), (void *)PT_REGS_PARM4(ctx));
bpf_printk("bpf_printk bpf_probe_read goroutine_struct.goid: %lld", gs.goid);
// test
void* ptr = (void *)PT_REGS_PARM4(ctx);
s64 goid2 = 0;
bpf_probe_read(&goid2, sizeof(goid2), (void *)(ptr+152));
bpf_printk("bpf_printk bpf_probe_read goid2: %lld", goid2);
return 0;
}
goroutine.h
#include "common.h"
struct stack {
u64 lo;
u64 hi;
};
struct gobuf {
u64 sp;
u64 pc;
u64 g;
u64 ctxt;
u64 ret;
u64 lr;
u64 bp;
};
/*
go version go1.17.2 linux/amd64
type stack struct {
lo uintptr
hi uintptr
}
type gobuf struct {
sp uintptr
pc uintptr
g uintptr
ctxt uintptr
ret uintptr
lr uintptr
bp uintptr
}
type g struct {
stack stack // offset known to runtime/cgo
stackguard0 uintptr // offset known to liblink
stackguard1 uintptr // offset known to liblink
_panic *_panic // innermost panic - offset known to liblink
_defer *_defer // innermost defer
m *m // current m; offset known to arm liblink
sched gobuf
syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
stktopsp uintptr // expected sp at top of stack, to check in traceback
param unsafe.Pointer
atomicstatus uint32
stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
goid int64
}
*/
struct g {
struct stack stack;
u64 stackguard0;
u64 stackguard1;
u64 _panic;
u64 _defer;
u64 m;
struct gobuf sched ;
u64 syscallsp;
u64 syscallpc;
u64 stktopsp;
u64 param;
u32 atomicstatus;
u32 stackLock;
s64 goid; // Here it is!
};
When I run my program , cat /sys/kernel/debug/tracing/trace_pipe output like this , get the wrong go id:
<...>-1336127 [000] d... 20113210.986990: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct->goid: 4938558469562467144
<...>-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct.goid: 4938558469562467144
<...>-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goid2: 4938558469562467144
Blockquote

I found a solution:
my golang version is 1.17.2, amd64. and the amd64 architecture uses the following sequence of 9 registers for integer arguments and results:
RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11
runtime.newproc1 which in go 1.17.2 has 5 args. callergp *g is the 4th. when I gdb my userspace program, it use rdi register to save ptr addr of callergp *g.
so use PT_REGS_PARM1 is the right way. because (#define PT_REGS_PARM1(x) ((x)->rdi))
after all, the code like this :
SEC("uprobe/runtime.newproc1")
int uprobe_runtime_newproc1(struct pt_regs *ctx) {
u32 key = 2;
u64 initval = 1, *valp;
valp = bpf_map_lookup_elem(&uprobe_map, &key);
if (!valp) {
bpf_map_update_elem(&uprobe_map, &key, &initval, BPF_ANY);
return 0;
}
__sync_fetch_and_add(valp, 1);
// retrieve output parameter
struct g gs;
bpf_probe_read(&gs, sizeof(gs), (void *)PT_REGS_PARM1(ctx));
bpf_printk("uprobe_runtime_newproc1 bpf_printk bpf_probe_read goroutine_struct.goid: %lld", gs.goid);
return 0;
}

Related

Get PE build version from resource directory in UEFI bootloader

I'm trying to get the build number from a 64-bit PE file (from the resource directory), namely the Windows bootmanager (bootmgfw.efi), using an UEFI bootloader. Currently I'm doing the following: Load the PE file from disk using BootServices->LoadImage and get the image base address using BootServices->OpenProtocol. Then I'm using the base address for the function below (HbeGetBuildNumber). Note that this exact function works flawless on bootmgfw.efi if I load the file using MapViewOfFile from a Windows executable, but not when using it inside my bootloader.
Code to obtain the build number by image base address:
#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_NT_SIGNATURE 0x00004550
#define TRUE 1
#define FALSE 0
#define NULL 0
#define FIELD_OFFSET(type, field) ((long)(long long)&(((type *)0)->field))
#define MAKEINTRESOURCE(i) ((PWSTR)((DWORD_PTR)((WORD)(i))))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define RT_VERSION MAKEINTRESOURCE(16)
#define DUMMYSTRUCTNAME
#define DUMMYUNIONNAME
#define DUMMYSTRUCTNAME2
#define DUMMYUNIONNAME2
typedef int BOOL, * PBOOL;
typedef unsigned short WCHAR_T, *PWSTR;
typedef unsigned long DWORD, *PDWORD;
typedef unsigned long long DWORD_PTR, * PDWORD_PTR;
typedef unsigned char BYTE, * PBYTE;
typedef BYTE UCHAR, * PUCHAR;
typedef void* PVOID;
typedef unsigned short WORD, *PWORD;
typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
DWORD BeginAddress;
DWORD EndAddress;
union {
DWORD UnwindInfoAddress;
DWORD UnwindData;
} DUMMYUNIONNAME;
} RUNTIME_FUNCTION, * PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, * _PIMAGE_RUNTIME_FUNCTION_ENTRY;
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[8];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, * PIMAGE_SECTION_HEADER;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, * PIMAGE_DATA_DIRECTORY;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
unsigned long long ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
unsigned long long SizeOfStackReserve;
unsigned long long SizeOfStackCommit;
unsigned long long SizeOfHeapReserve;
unsigned long long SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER64, * PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER;
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, * PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
long e_lfanew;
} IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries;
WORD NumberOfIdEntries;
} IMAGE_RESOURCE_DIRECTORY, * PIMAGE_RESOURCE_DIRECTORY;
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union {
struct {
DWORD NameOffset : 31;
DWORD NameIsString : 1;
} DUMMYSTRUCTNAME;
DWORD Name;
WORD Id;
} DUMMYUNIONNAME;
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory : 31;
DWORD DataIsDirectory : 1;
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, * PIMAGE_RESOURCE_DIRECTORY_ENTRY;
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData;
DWORD Size;
DWORD CodePage;
DWORD Reserved;
} IMAGE_RESOURCE_DATA_ENTRY, * PIMAGE_RESOURCE_DATA_ENTRY;
typedef struct tagVS_FIXEDFILEINFO
{
DWORD dwSignature;
DWORD dwStrucVersion;
DWORD dwFileVersionMS;
DWORD dwFileVersionLS;
DWORD dwProductVersionMS;
DWORD dwProductVersionLS;
DWORD dwFileFlagsMask;
DWORD dwFileFlags;
DWORD dwFileOS;
DWORD dwFileType;
DWORD dwFileSubtype;
DWORD dwFileDateMS;
DWORD dwFileDateLS;
} VS_FIXEDFILEINFO;
typedef struct tag_VS_VERSIONINFO
{
unsigned short wLength;
unsigned short wValueLength;
unsigned short wType;
WCHAR_T szKey[17];
VS_FIXEDFILEINFO Value;
}VS_VERSIONINFO, * PVS_VERSIONINFO;
PVOID HbGetPtr2(DWORD_PTR relativeAddress, PIMAGE_NT_HEADERS64 ntHeaders, DWORD_PTR imageBase)
{
PVOID retVal = NULL;
BOOL found = FALSE;
PIMAGE_SECTION_HEADER secHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)ntHeaders + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + ntHeaders->FileHeader.SizeOfOptionalHeader);
for (DWORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, secHeader++)
{
if ((relativeAddress >= secHeader->VirtualAddress)
&& (relativeAddress < (secHeader->VirtualAddress + secHeader->Misc.VirtualSize)))
{
found = TRUE;
break;
}
}
if (found)
retVal = (PVOID)(imageBase + relativeAddress - (int)(secHeader->VirtualAddress - secHeader->PointerToRawData));
return retVal;
}
//Get build number for PE file
unsigned long HbeGetBuildNumber(void* imageBase)
{
DWORD retVal = 0;
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)imageBase);
PIMAGE_NT_HEADERS64 ntHeaders = (PIMAGE_NT_HEADERS64)((DWORD_PTR)imageBase + dosHeader->e_lfanew);
PIMAGE_DATA_DIRECTORY dataDir = NULL;
PIMAGE_RESOURCE_DIRECTORY resDir = NULL, subDir = NULL;
PIMAGE_RESOURCE_DIRECTORY_ENTRY dirEntry = NULL, versionDir = NULL;
PIMAGE_RESOURCE_DATA_ENTRY dataEntry = NULL;
PVS_VERSIONINFO versionInfo;
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
goto Done;
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE)
goto Done;
dataDir = &ntHeaders->OptionalHeader.DataDirectory[2];
resDir = (PIMAGE_RESOURCE_DIRECTORY)HbGetPtr2(dataDir->VirtualAddress, ntHeaders, (DWORD_PTR)imageBase);
dirEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resDir + 1);
if (!resDir)
goto Done;
for (DWORD i = 0; i < resDir->NumberOfIdEntries; i++, dirEntry++)
{
if (dirEntry->Id == (WORD)RT_VERSION
&& dirEntry->DataIsDirectory)
{
subDir = (PIMAGE_RESOURCE_DIRECTORY)((dirEntry->OffsetToData & 0x7FFFFFFF) + (DWORD_PTR)resDir);
versionDir = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(subDir + 1);
if (versionDir->DataIsDirectory)
{
subDir = (PIMAGE_RESOURCE_DIRECTORY)((versionDir->OffsetToData & 0x7FFFFFFF) + (DWORD_PTR)resDir);
versionDir = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(subDir + 1);
dataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((versionDir->OffsetToData & 0x7FFFFFFF) + (DWORD_PTR)resDir);
versionInfo = (PVS_VERSIONINFO)HbGetPtr2(dataEntry->OffsetToData, ntHeaders, (DWORD_PTR)imageBase);
if (versionDir)
retVal = HIWORD(versionInfo->Value.dwFileVersionLS);
}
}
}
Done:
return retVal;
}
Through debugging I figured out that the error occurs when trying to locate PIMAGE_RESOURCE_DIRECTORY using HbGetPtr2, since resDir->NumberOfIDEntries should have the value 4, but in the error case it contains multiple thousand (obviously wrong). Where is my error in this code? Why does the code work on the same file if I run it from a Windows exe but not from my bootloader? Is there a difference between BootServices->LoadImage (Bootloader) and MapViewOfFile (Windows executable)?
Edit:
This is how I use the code above in my Windows executable (working):
HANDLE hFile;
HANDLE hFileMapping;
LPVOID lpFileBase;
hFile = CreateFileW(L"C:\\Users\\XY\Reverse Engineering\\EFI\\bootmgfw.efi", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE)
return 1;
hFileMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMapping == 0)
{
CloseHandle(hFile);
return 1;
}
lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
if (lpFileBase == 0)
{
CloseHandle(hFileMapping);
CloseHandle(hFile);
return 1;
}
std::cout << HbeGetBuildNumber(lpFileBase) << std::endl;
This is how I use the code above in my bootloader (fails):
BOOL retVal = FALSE;
PEFI_BOOT_SERVICES services = systemTable->BootServices;
PEFI_LOADED_IMAGE loadedImage;
PEFI_LOADED_IMAGE_PROTOCOL winImageInfo;
PEFI_DEVICE_PATH winPath;
EFI_HANDLE winHandle = NULL;
EFI_STATUS loadSuccess = -1;
DWORD bootmgfwBuildNumber = 0;
if (EFI_ERROR(services->HandleProtocol(curImageHandle, &gEfiLoadedImageProtocolGuid, &loadedImage)))
goto Done;
if (!(winPath = FileDevicePath(loadedImage->DeviceHandle, L"EFI\\Microsoft\\Boot\\bootmgfw.efi")))
goto Done;
if (EFI_ERROR(loadSuccess = services->LoadImage(TRUE, curImageHandle, winPath, NULL, 0, &winHandle)))
goto Done;
DBGMSG(L"Loaded bootmgfw.efi\n");
FreePool(winPath);
if (EFI_ERROR(services->OpenProtocol(winHandle, &gEfiLoadedImageProtocolGuid, (PVOID *)&winImageInfo, curImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL)))
goto Done;
DBGMSG(L"Got file information\n");
if (!(bootmgfwBuildNumber = HbGetBuildNumber(winImageInfo->ImageBase))) //ERROR: Returns 0
goto Done
//...
Done:
DBGMSG(L"Start image\n");
if (!EFI_ERROR(loadSuccess) && !EFI_ERROR(services->StartImage(winHandle, 0, NULL)))
retVal = TRUE;
return retVal;

After GCC version upgrade, unexpected behavior of a reenterable function may be caused by GCC optimization

Upgraded the gcc version from 4.6.3 to 8.2.0. The following function doesn't work without the memory barrier.
static u32 checksumPsedoHeader(u16 size, u32 src_addr, u32 dest_addr)
{
const u16 protocol_udp = 17;
u32 checksum = 0;
// Add UDP pseudo header.
struct PseudoHeader
{
unsigned int src;
unsigned int dst;
unsigned char zero;
unsigned char proto;
u16 length;
} pseudoHeader;
// Initalize the array. Using an uninitialized
// array is not acceptable by newer compiler versions.
memset((void*)&pseudoHeader, 0, sizeof(pseudoHeader));
pseudoHeader.dst = htonl(dest_addr);
pseudoHeader.src = htonl(src_addr);
pseudoHeader.zero = 0;
pseudoHeader.proto = protocol_udp;
pseudoHeader.length = htons(size);
u16 *pseudo_header_p = (u16*)&pseudoHeader;
asm volatile("" ::: "memory"); // The function doesn't work without this line after GCC upgration.
for (u32 i = 0; i < sizeof(pseudoHeader); i += 2)
{
checksum = checksum + *pseudo_header_p;
pseudo_header_p++;
}
return checksum;
}
Why does it need a memory barrier here? The pointer type conversion make the compiler did the wrong optimization(after the memory barrier, the for loop fetch the values from memories but all the values have not been flushed to the memories)?

How to exactly modify the Local Descriptor table on 64 bits Windows?

The aim is to enable 16 bits segments for 16 bits addressing like on 64 bits Linux Kernel with the modify_ldt() system call.
I was unable to find if Cygwin provides a wrapper and I have only barely an idea that it’s about something like NtSetLdtEntries with :
typedef struct
{
ULONG Start;
ULONG Length;
LDT_ENTRY LdtEntries[1];
} PROCESS_LDT_INFORMATION, *PPROCESS_LDT_INFORMATION;
Please note this is not related to the vm86 mode (which is a different method of doing it used by Microsoft on 32 bits systems). And As stated above, this way is used on Linux for running 16 bits code in protected mode without any emulation. See CONFIG_X86_16BIT for more informations.
Of course, if it’s not supported by default it’s Ok to modify system files.
on x86-based windos possible (tested on xp and win 8.1 x86) set several descriptors in LDT table and use this. this can be done via NtSetInformationProcess with ProcessLdtInformation (undocumented) or, if we need set only 1 or 2 selectors - more easy use undocumented api:
EXTERN_C
__declspec(dllimport)
NTSTATUS
NTAPI
NtSetLdtEntries
(
__in_opt ULONG Selector1,
__in SEGMENT_ENTRY LdtEntry1,
__in_opt ULONG Selector2,
__in SEGMENT_ENTRY LdtEntry2
);
so we need allocate 1 or more SEGMENT_ENTRY (or LDT_ENTRY - declared in winnt.h), allocate memory for segment, and call api. I did not pay much attention for 16 bit code and fill actual descriptors, check only memory fill via LDT selector (assigned to ES) and then read it via "plain" selector.
typedef struct SEGMENT_ENTRY
{
ULONG LimitLow : 16;
ULONG BaseLow : 16;
ULONG BaseMid : 8;
ULONG Type : 4;
ULONG IsGegment : 1;// = 1
ULONG DPL : 2;
ULONG P : 1;// Present
ULONG LimitHi : 4;
ULONG AVL : 1;// Available For software use
ULONG L : 1;// Long-mode segment
ULONG D : 1;// Default operand size
ULONG G : 1;// Granularity
ULONG BaseHi : 8;
}*PSEGMENT_ENTRY;
typedef struct PROCESS_LDT_INFORMATION
{
ULONG StartSelector;
ULONG Length;
SEGMENT_ENTRY LdtEntries[];
} *PPROCESS_LDT_INFORMATION;
EXTERN_C
__declspec(dllimport)
NTSTATUS
NTAPI
NtSetLdtEntries
(
__in_opt ULONG Selector1,
IN SEGMENT_ENTRY LdtEntry1,
__in_opt ULONG Selector2,
IN SEGMENT_ENTRY LdtEntry2
);
NTSTATUS TestLdt()
{
PVOID BaseAddress = 0;
SIZE_T RegionSize = 0x100000;//1mb
NTSTATUS status = NtAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (0 <= status)
{
#if 1
SEGMENT_ENTRY LdtEntry = {};
LdtEntry.LimitLow = 0xffff;
LdtEntry.BaseLow = ((ULONG_PTR)BaseAddress) & 0xFFFF;
LdtEntry.BaseMid = ((ULONG_PTR)BaseAddress >> 16) & 0xff;
LdtEntry.BaseHi = ((ULONG_PTR)BaseAddress >> 24) & 0xff;
LdtEntry.P = 1;
LdtEntry.DPL = 3;
LdtEntry.IsGegment = 1;
LdtEntry.Type = 2;//ldt
status = NtSetLdtEntries(8, LdtEntry, 0, LdtEntry);
#else
const ULONG cb = sizeof(PROCESS_LDT_INFORMATION) + 1 * sizeof(LDT_ENTRY);
PPROCESS_LDT_INFORMATION LdtInfo = (PPROCESS_LDT_INFORMATION)alloca(cb);
LdtInfo->Length = 1 * sizeof(LDT_ENTRY);
LdtInfo->StartSelector = 8;
SEGMENT_ENTRY* LdtEntry = LdtInfo->LdtEntries;
LdtEntry->LimitLow = 0xffff;
LdtEntry->BaseLow = ((ULONG_PTR)BaseAddress) & 0xFFFF;
LdtEntry->BaseMid = ((ULONG_PTR)BaseAddress >> 16) & 0xff;
LdtEntry->BaseHi = ((ULONG_PTR)BaseAddress >> 24) & 0xff;
LdtEntry->L = 0;
LdtEntry->D = 0;
LdtEntry->G = 0;
LdtEntry->AVL = 0;
LdtEntry->P = 1;
LdtEntry->DPL = 3;
LdtEntry->IsGegment = 1;
LdtEntry->Type = 2;//ldt
status = NtSetInformationProcess(NtCurrentProcess(), ProcessLdtInformation, LdtInfo, cb);
#endif
if (0 <= status)
{
DbgPrint("%s\n", BaseAddress); // print empty string
#ifdef _X86_
__asm {
push edi
mov ax,0xf
mov dx,es
mov es,ax
mov ecx,32
mov al,0x33
xor edi,edi
rep stosb
mov es,dx
pop edi
}
#endif
DbgPrint("%s\n", BaseAddress);// print 33333333...
}
NtFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE);
}
return status;
}
however This is valid only on x86-based windows systems.
if you call this on any x64 windows you got error STATUS_NOT_IMPLEMENTED. here windows not support LDT at all. and this can not be changed (even by modify system files. ?!)
more info - Local Descriptor Table on x64

for range loop with const& argument when the iterable itself is passed by a const& argument

If I pass a container by constant reference, is there a point in declaring a constant reference in a range-for loop, or will the elements in the loop automatically inherent that property?
i.e.
int foo(std::vector<int> const& vec) {
for (int const& el : vec)
// do something...
}
is the above equivalent to:
int foo(std::vector<int> const& vec) {
for (int el : vec)
// do something...
}
For a trivial scalar like an int, a const reference is just overhead - behind the scenes references are implemented with pointers, so a reference is really just a compiler-managed pointer and accessing it requires a dereference.
#include <vector>
extern void f1(int);
extern void f2(int const&);
int foo1(std::vector<int> const& v) {
for (int const& val: v) {
f1(val);
f2(val);
}
}
int foo2(std::vector<int> const& v) {
for (int val: v) {
f1(val);
f2(val);
}
}
Assembly output
foo2 produces this:
movl (%rbx), %edi ; val = *it
addq $4, %rbx ; ++it
movl %edi, 12(%rsp) ; save val on stack
call f1(int)
leaq 12(%rsp), %rdi ; load address of saved val
call f2(int const&)
Note the leaq for calling f2. We took a simple copy of the current vector value into edi, but then we also had to push it onto the stack so we could get an address to comply with the reference requirement for f2.
However in a different example, the compiler is perfectly capable of figuring out that a reference wasn't needed and just doing the right thing:
#include <vector>
#include <atomic>
int total;
int foo1(std::vector<int> const& v) {
for (int const& val: v) {
total += val;
}
}
int foo2(std::vector<int> const& v) {
for (int val: v) {
total += val;
}
}
both functions produce the same code, the compiler eliminated the reference.

No-overflow cast on x64

I have an existing C codebase that works on x86.
I'm now compiling it for x64.
What I'd like to do is cast a size_t to a DWORD, and throw an exception if there's a loss of data.
Q: Is there an idiom for this?
Here's why I'm doing this:
A bunch of Windows APIs accept DWORDs as arguments, and the code currently assumes sizeof(DWORD)==sizeof(size_t). That assumption holds for x86, but not for x64. So when compiling for x64, passing size_t in place of a DWORD argument, generates a compile-time warning.
In virtually all of these cases the actual size is not going to exceed 2^32. But I want to code it defensively and explicitly.
This is my first x64 project, so... be gentle.
see boost::numeric_cast
http://www.boost.org/doc/libs/1_33_1/libs/numeric/conversion/doc/numeric_cast.html
I just defined a function to perform the cast.
I included an assert-like behavior to insure I'm not silently rubbishing pointers.
DWORD ConvertSizeTo32bits(size_t sz, char *file, int line)
{
if (!(0 <= sz && sz <= INT32_MAX)) {
EmitLogMessage("Invalid Pointer size: %d file(%s) line(%d)",
sz, file, line);
ExitProcess( 0 );
}
return (DWORD) sz;
}
#define size_t_to_DWORD(st,dw) if ((DWORD)(st) != st) RaiseException(exLossOfData, 0, 0, NULL); else dw = (DWORD)(st)
size_t st;
DWORD dw;
st = 0xffffffff;
size_t_to_DWORD(st,dw); // this succeeds
st = 0xffffffff1;
size_t_to_DWORD(st,dw); // this throws
EDIT:
Or better yet, do this so you can use it in an expression:
DWORD MyRaiseException()
{
RaiseException(1, 0, 0, NULL);
return 0;
}
#define size_t_to_DWORD(st) (DWORD)(st) != (st) ? MyRaiseException() : (DWORD)(st)
void main(void)
{
size_t st;
DWORD dw;
st = 0xffffffff1;
dw = size_t_to_DWORD(st);
printf("%u %u\n", st, dw);
}

Resources