While browsing the SDL source code that interfaces with the system window manager, I encountered struct SDL_VideoDevice with a mystical Uint8 window_magic field. There doesn't seem to be any documentation for this field. What is the purpose of this field? It plays a role in the following functions/macros (all defined in SDL_video.c).
The macro CHECK_WINDOW_MAGIC:
#define CHECK_WINDOW_MAGIC(window, retval) \
if (!_this) { \
SDL_UninitializedVideo(); \
return retval; \
} \
if (!window || window->magic != &_this->window_magic) { \
SDL_SetError("Invalid window"); \
return retval; \
}
The function SDL_GetWindowWMInfo:
SDL_GetWindowWMInfo(SDL_Window * window, struct SDL_SysWMinfo *info)
{
CHECK_WINDOW_MAGIC(window, SDL_FALSE);
if (!info) {
SDL_InvalidParamError("info");
return SDL_FALSE;
}
info->subsystem = SDL_SYSWM_UNKNOWN;
if (!_this->GetWindowWMInfo) {
SDL_Unsupported();
return SDL_FALSE;
}
return (_this->GetWindowWMInfo(_this, window, info));
}
The function SDL_CreateWindow:
SDL_Window *
SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
{
...
window->magic = &_this->window_magic;
...
}
_this is a pointer to an SDL_VideoDevice, which is initialized when the user calls SDL_Init. When the user calls SDL_CreateWindow, SDL assignes window->magic to the address of _this->window_magic. As far as I can tell, _this->window_magic is never initialized anywhere in SDL. What could possibly be the role of this value?
It uses address of _this->window_magic, not its value. Variable don't have to be initialised to have an address, especially if it is a structure field.
Its purpose is to quickly check against uninitialised window, or not a window at all. No guarantees but hit rate is very high - it is very unlikely that anything will accidentially match the address of SDL's internal structure.
Related
I have a function which needs to parse some arguments and several if clauses inside it need to perform similar actions. In order to reduce typing and help keep the code readable, I thought I'd use a lambda to encapsulate the recurring actions, but I'm having trouble finding sufficient info to determine whether I'm mistakenly invoking undefined behavior or what I need to do to actualize my approach.
Below is a simplified code snippet of what I have currently:
int foo(int argc, char* argv[])
{
Using ss = std::istringstream;
auto sf = [&](ss&& stream) -> ss& {
stream.exceptions(ss::failbit);
return stream;
};
int retVal = 0;
bool valA = false;
bool valB = false;
try
{
for(int i=1; i < argc; i++)
{
std::string arg( argv[i] );
if( !valA )
{
valA = true;
sf( ss(arg) ) >> myInt;
}
else
if( !valB )
{
valB = true;
sf( ss(arg) ) >> std::hex >> myOtherInt;
}
}
}
catch( std::exception& err )
{
retVal = -1;
std::cerr << err.what() << std::endl;
}
return retVal;
}
First, based on what I've read, I don't think that specifying the lambda argument as an rvalue reference (ss&&) is doing quite what I want it to do, however, trying to compile with it declared as a normal reference (ss&) failed with the error cannot bind non-const lvalue reference of type 'ss&'. Changing ss& to ss&& got rid of the error and did not produce any warnings, but I'm not convinced that I'm using that construct correctly.
I've tried reading up on the various definitions for each, but the wording is a bit confusing.
I guess ultimately my questions are:
Can I expect the lifetime of my temporary ss(arg) object to extend through the entire extraction expression?
What is the correct way to define a lambda such that I can use the lambda in the way I demonstrate above, assuming that such a thing is actually possible?
I'm trying to load an eBPF object in the kernel with libbpf, with no success, getting the error specified in the title. But let me show how simple my BPF *_kern.c is.
SEC("entry_point_prog")
int entry_point(struct xdp_md *ctx)
{
int act = XDP_DROP;
int rc, i = 0;
struct global_vars *globals;
struct ip_addr addr = {};
struct some_key key = {};
void *temp;
globals = bpf_map_lookup_elem(&globals_map, &i);
if (!globals)
return XDP_ABORTED;
rc = some_inlined_func(ctx, &key);
addr = key.dst_ip;
temp = bpf_map_lookup_elem(&some_map, &addr);
switch(rc)
{
case 0:
if(temp)
{
// no rocket science here ...
} else
act = XDP_PASS;
break;
default:
break;
}
return act; // this gives the error
//return XDP_<whatever>; // this works fine
}
More precisely, the libbpf error log is the following:
105: (bf) r4 = r0
106: (07) r4 += 8
107: (b7) r8 = 1
108: (2d) if r4 > r3 goto pc+4
R0=inv40 R1=inv0 R2=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R3=pkt_end(id=0,off=0,imm=0) R4=inv48 R5=inv512 R6=inv1 R7=inv17 R8=inv1 R10=fp0,call_-1 fp-16=0 fp-32=0 fp-40=0
109: (69) r3 = *(u16 *)(r0 +2)
R0 invalid mem access 'inv'
I really don't see any problem here. I mean, this is so so simple, and yet it breaks. Why shouldn't this work? What am I missing? Either the verifier went crazy, or I'm doing something very stupid.
Ok, so, after 3 days, more precisely 3 x 8 hrs = 24 hrs, worth of code hunting, I think I've finally found the itching problem.
The problem was in the some_inlined_func() all along, it was more tricky then challenging. I'm writing down here a code template explaining the issue, so others could see and hopefully spend less then 24 hrs of headache; I went through hell for this, so stay focused.
__alwais_inline static
int some_inlined_func(struct xdp_md *ctx, /* other non important args */)
{
if (!ctx)
return AN_ERROR_CODE;
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth;
struct iphdr *ipv4_hdr = NULL;
struct ipv6hdr *ipv6_hdr = NULL;
struct udphdr *udph;
uint16_t ethertype;
eth = (struct ethhdr *)data;
if (eth + 1 > data_end)
return AN_ERROR_CODE;
ethertype = __constant_ntohs(eth->h_proto);
if (ethertype == ETH_P_IP)
{
ipv4_hdr = (void *)eth + ETH_HLEN;
if (ipv4_hdr + 1 > data_end)
return AN_ERROR_CODE;
// stuff non related to the issue ...
} else if (ethertype == ETH_P_IPV6)
{
ipv6_hdr = (void *)eth + ETH_HLEN;
if (ipv6_hdr + 1 > data_end)
return AN_ERROR_CODE;
// stuff non related to the issue ...
} else
return A_RET_CODE_1;
/* here's the problem, but ... */
udph = (ipv4_hdr) ? ((void *)ipv4_hdr + sizeof(*ipv4_hdr)) :
((void *)ipv6_hdr + sizeof(*ipv6_hdr));
if (udph + 1 > data_end)
return AN_ERROR_CODE;
/* it actually breaks HERE, when dereferencing 'udph' */
uint16_t dst_port = __constant_ntohs(udph->dest);
// blablabla other stuff here unrelated to the problem ...
return A_RET_CODE_2;
}
So, why it breaks at that point? I think it's because the verifier assumes ipv6_hdr could potentially be NULL, which is utterly WRONG because if the execution ever gets to that point, that's only because either ipv4_hdr or ipv6_hdr has been set (i.e. the execution dies before this point if it's the case of neither IPv4 nor IPv6). So, apparently, the verifier isn't able to infer that. However, there's a catch, it is happy if the validity of also ipv6_hdr is explicitly checked, like this:
if (ipv4_hdr)
udph = (void *)ipv4_hdr + sizeof(*ipv4_hdr);
else if (ipv6_hdr)
udph = (void *)ipv6_hdr + sizeof(*ipv6_hdr);
else return A_RET_CODE_1; // this is redundant
It also works if we do this:
// "(ethertype == ETH_P_IP)" instead of "(ipv4_hdr)"
udph = (ethertype == ETH_P_IP) ? ((void *)ipv4_hdr + sizeof(*ipv4_hdr)) :
((void *)ipv6_hdr + sizeof(*ipv6_hdr));
So, it seems to me there's something strange about the verifier here, because it's not smart enough (maybe neither it needs to be?) to realize that if it ever gets to this point, it's only because ctx refers either an IPv4 or IPv6 packet.
How does all of this explain the complaining over return act; within the entry_point()? Simple, just bear with me. The some_inlined_func() isn't changing ctx, and its remaining args aren't used either by entry_point(). Thus, in case of returning act, as it depends on the some_inlined_func() outcome, the some_inlined_func() gets executed, with the verifier complaining at that point. But, in case of returning XDP_<whatever>, as the switch-case body, and neither the some_inlined_func(), doesn't change the internal state of the entry_point() program/function, the compiler (with O2) is smart enough to realize that there's no point in producing assembly for some_inlined_func() and the whole switch-case (that's the O2 optimization over here). Therefore, to conclude, in case of returning XDP_<whatever>, the verifier was happy as the problem actually lies into some_inlined_func() but the actual produced BPF assembly doesn't have anything of that, so the verifier didn't checked some_inlined_func() because there wasn't any in the first place. Makes sense?
Is such BPF "limitation" known? Is out there any document at all stating such known limitations? Because I didn't found any.
For visiting a variant using a lambda based visitor I came up to the boost.preprocessor for generating the boilerplate required:
#include <boost/preprocessor.hpp>
#define MY_OVERLOAD(r, data, elem) \
[](elem const& t) { return false; },
#define MY_OVERLOAD_SEQ_MEMBER(typeSeq) \
BOOST_PP_SEQ_FOR_EACH(MY_OVERLOAD, ~, typeSeq)
#define MY_OVERLOAD_MEMBER(typeSeq) \
MY_OVERLOAD_SEQ_MEMBER(typeSeq)
int main()
{
auto visitor = hana::overload(
#if 0 // like to have:
[](int t) { return false; },
[](double t) { return false; },
[](std::string const& t) { false; }
#else
MY_OVERLOAD_MEMBER((int)(double)(std::string))
#endif
);
...
}
This expands as expected so far but failed at last element - there is a trailing comma which fails to compile. I know about BOOST_PP_COMMA_IF, BOOST_PP_ENUM... which do require the number of elements to generate. But this doesn't hold for my use case - the type list is different for each type of visitor of course. Further, I don't be restricted to list the arguments in the manner shown here, a comma separted list as macro argument is also sufficient...
Also note, this code shows only the concept I want to use - in real I don't want to catch POD by reference.
BTW; is the kind of expanding required horizontal or vertically in the term of boost.preprocessor? From feeling horizontal, isn't it?
I was reading on the explanation of container_of macro and I got most of it except one thing. How does inode->I_cdev point to cdev?
To be sure the cdev here is the character device we initialized in our cdev_init call .
one more thing to add
dev in "struct cdev" is DD point of view and dev_t in "struct inode" is Fs( file system) point of view.
also the Struct inode ( part of fs.h) ---> struct cdev(part of cdev.h & Member object of struct Inode ) ---> ( major , minor )
& the Struct Dev ( user defined ) ---> struct cdev---> ( major , minor)
both reaches the same point .
if you look in chrdev_open function under fs/char_dev.c file you will see that before opening any character device, its respective i_cdev structure is populated in inode's structure.
p = inode->i_cdev;
if (!p) {
struct kobject *kobj;
int idx;
spin_unlock(&cdev_lock);
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
if (!kobj)
return -ENXIO;
new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev;
if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list);
new = NULL;
} else if (!cdev_get(p))
ret = -ENXIO;
} else if (!cdev_get(p))
ret = -ENXIO;
Well for the inode itself, it must be created when a device node is created.
I dont know much about kobjects, but its some how getting the cdev using kobj, and where we associated that kobject with this perticular device? the answer lies in cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}
I'm encountering a problem when trying to pass a parameter through my program via the command line (eg. -w 1280 -h 1024) while attempting to utilize WinMain. I've looked through every topic I could find, and have created code that builds and runs, but the parameters are ignored completely!
My Code:
LPWSTR *szArgList;
int argCount;
szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
for(int i = 1;i < argCount;i++)
{
if(i + 1 != argCount)
{
if(szArgList[i] == L"-w")
{
width = _wtoi(szArgList[i+1]);
}
else if(szArgList[i] == L"-h")
{
height = _wtoi(szArgList[i+1]);
}
}
}
MSG msg;
BOOL done=FALSE;
if(MessageBox(NULL,"Fullscreen?", "my window", MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE;
}
if(!CreateGLWindow("Window",width,height,16,fullscreen))
{
return 0;
}
I'm attempting to pass it as "window.exe -w 800 -h 600" (without quotes, of course)
Anything i'm missing within my sleep-depraved code?
szArgList[i] == L"-w"
szArgList[i] == L"-h"
C and C++ will compare by pointer instead of character. use strcmp.