Strange memory layout with Option<unsafe fn ...> within a struct - c

I'm using JNI definitions from here. I create a JNINativeInterface_ with most members initialized to None. I then run native code which uses the RegisterNatives field of the aforementioned struct. I initialized RegisterNatives and surrounding fields as such:
SetDoubleArrayRegion: unsafe { transmute(0xdeadbeaf as u64) },
RegisterNatives: Some(register_natives),
UnregisterNatives: unsafe { transmute(0xdeadbeaf as u64) },
register_natives is defined like so(this matches the library type exactly):
unsafe extern "system" fn register_natives(env: *mut sys::JNIEnv,
clazz: jclass,
methods: *const JNINativeMethod,
nMethods: jint) -> jint {
unimplemented!()
}
The native code that uses the struct segfaults(and seems to get a null ptr instead of register_natives).
The relevant part of the struct looks like so under GDB:
0x7ffcf5f4a5b8: 0x0 0x0 0x0 0x0
0x7ffcf5f4a5c8: 0xdeadbeaf 0x0 0x43fd9950 0x55ea
0x7ffcf5f4a5d8: 0xdeadbeaf 0x0 0x0 0x0
0x7ffcf5f4a5e8: 0x0 0x0 0x0 0x0
I'm confused as to exactly what I am looking at since I was expecting 0xdeadbeaf , followed by a 64 bit pointer, followed by 0xdeadbeaf, but as you can see that is not what I get. Am I wrong about my assumptions as to how option will be represented behind the scenes? Why does bindgen/the aformentioned library seem to thing that Option will lead to a compatible interface?

[...] I was expecting 0xdeadbeaf, followed by a 64 bit pointer, followed by 0xdeadbeaf, but as you can see that is not what I get.
We must not be seeing the same thing, because I do see that.
0x7ffcf5f4a5c8: 0xdeadbeaf 0x0 0x43fd9950 0x55ea
0x7ffcf5f4a5d8: 0xdeadbeaf 0x0 0x0 0x0
Each hex number is a 32-bit integer, so you have to take two of them to make a 64-bit integer. The first is 0x00000000deadbeaf, the second is 0x000055ea43fd9950 (your register_natives function, presumably) and the third is 0x00000000deadbeaf again. (It's also "obvious" from the addresses: a 64-bit integer takes 8 bytes, so it takes two to take 0x10 bytes. Therefore, there are two 64-bit integer per line.)
The reason the program segfaults may be because letting a panic unwind through foreign code is undefined behavior. Try changing your register_natives function to something that doesn't panic.

Related

Keep having an error when using static array which is not initialized

I am using 16bit microcontroller which uses gcc4.5 compiler, 32kbyte flash/1kbyte ram. compiling is fine without error, memory is reaching to it's limit but it has some vacancy on both flash/ram and works just fine
However, when i define static array without initialization it causes system to die. I cannot use debugger to watch what's going on inside my system.
I didn't even use the array other than define it. so I assume compiler should skip below code but it kept making a problem.
typedef enum
{
PTC_VOLT_ERROR = 0,
PTC_LINE_ERROR,
PTC_ERROR,
PTC_PCB1TEMP_ERROR,
PTC_PCB2TEMP_ERROR,
PTC_COMM_ERROR,
PTC_SELF_DIAG_ERROR,
PTC_PCB1TEMPSSR_ERROR,
PTC_PCB2TEMPSSR_ERROR,
NUM_OF_PTC_ERROR_TYPE
}PtcErrorTypeInfo_t;
(...)
static INT8U preErrorInfo[NUM_OF_PTC_ERROR_TYPE]; // This cause system to die
//static INT8U preErrorInfo[NUM_OF_PTC_ERROR_TYPE]={0,}; // This is ok
Then I tried with non-static global arry and found it was working well.
INT8U preErrorInfo[NUM_OF_PTC_ERROR_TYPE]; // This is ok
static INT8U preErrorInfo[2]; // This is also ok
static INT8U preErrorInfo[9]; // This is not working
static INT8U preErrorInfo[2]; // This is working
I want to know what could make my system down?
→→→ It turned out that it had something to do with overflow and trap error.
first>> when I compile without static, in the map file
.data.g_PtcProtectionState
0x0000007e 0x2 obj/EcuProtectCTR.o
0x0000007e _g_PtcProtectionState
.data.preErrorInfo
0x00000080 0x9 obj/EcuProtectCTR.o
.data.u1_ErrorDeterTmr
0x00000089 0x1 obj/EcuProtectCTR.o
.data.pendingCalibOn
0x0000008a 0x6 obj/EcuSsrInputCTR.o
0x0000008a _pendingCalibOn
(...)
.bss.g_u4_ErrorCntrInfo
0x00000144 0x4 obj/EcuProtectCTR.o
**.bss.g_u1_TmrInt
0x00000148 0x1 obj/SchM.o
0x00000148 _g_u1_TmrInt**
.bss.u1_ElapsedTime
0x00000149 0x1 obj/SchM.o
it makes preErrorInfo[9] array seats in the data area.
second>> if i compile with 'static'
.bss.g_u4_ErrorCntrInfo
0x0000013c 0x4 obj/EcuProtectCTR.o
.bss.preErrorInfo
0x00000140 0x9 obj/EcuProtectCTR.o
.bss.g_u1_TmrInt
0x00000149 0x1 obj/SchM.o
0x00000149 _g_u1_TmrInt
preErrorInfo arry nicely seats on bss area. However, there is a variable 'g_u1_TmrInt' which keeps causing a problem. this timer-interrupt geard variable keeps making an overflow.
since 'static preErrorInfo array' will seat right above 'g_u1_TmrInt' somehow it could be messed up with 'static preErrorInfo array' when overflowing.
But I wonder why it didn't cause a problem when it comes to a case 'first>>' which has a 'g_u4_ErrorCntrInfo' 4byte array seated right above 'g_u1_TmrInt' and it would surely make the same overflow error.
※ when I change 'g_u1_TmrInt' type to 16bit(no-overflow and no error), it would look like this and work like charm.
.bss.preErrorInfo
0x00000140 0x9 obj/EcuProtectCTR.o
*fill* 0x00000149 0x1
.bss.g_u1_TmrInt
0x0000014a 0x2 obj/SchM.o
0x0000014a _g_u1_TmrInt
.bss.u1_ElapsedTime
0x0000014c 0x2 obj/SchM.o
.bss.SchConfig

perlbench results in segfault outside the SPEC 2006 harness

This might be overly specific, but posting here as it might help someone else who's trying to compile/run the SPEC 2006 benchmarks outside the default SPEC benchmark harness. (Our reason of doing this is comparing compiling strategies and code coverage, while the SPEC harness is focused on performance of the resulting code only).
When performing a ref run of perlbench the benchmark crashes with a segmentation fault:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004f6868 in S_regmatch (prog=0x832144)
at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:3024
3024 PL_reg_start_tmp[n] = locinput;
(gdb) bt
#0 0x00000000004f6868 in S_regmatch (prog=0x832144)
at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:3024
#1 0x00000000004f22cf in S_regtry (prog=0x8320c0, startpos=0x831e70 "o")
at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:2196
#2 0x00000000004eba71 in Perl_regexec_flags (prog=0x8320c0, stringarg=0x831e70 "o", strend=0x831e71 "",
strbeg=0x831e70 "o", minend=0, sv=0x7e2528, data=0x0, flags=3)
at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:1910
#3 0x00000000004b33bb in Perl_pp_match ()
at <path-to-spec>/CPU2006/400.perlbench/src/pp_hot.c:1340
#4 0x00000000004fcde4 in Perl_runops_standard ()
at <path-to-spec>/CPU2006/400.perlbench/src/run.c:37
#5 0x000000000046bf57 in S_run_body (oldscope=1)
at <path-to-spec>/CPU2006/400.perlbench/src/perl.c:2017
#6 0x000000000046b9f6 in perl_run (my_perl=0x7bf010)
at <path-to-spec>/CPU2006/400.perlbench/src/perl.c:1934
#7 0x000000000047add2 in main (argc=4, argv=0x7fffffffe178, env=0x7fffffffe1a0)
at <path-to-spec>/CPU2006/400.perlbench/src/perlmain.c:98
The execution environment is 64-bit Linux and the behaviour is observed with both the latest gcc and clang.
What causes this crash?
The segfault is caused by a garbage value of the variable n on the pointed out line. Inspecting the code shows that the value comes from the field arg1 of an object of type:
struct regnode_1 {
U8 flags;
U8 type;
U16 next_off;
U32 arg1;
};
Inspecting the memory location of the object shows that it is not packed, i.e. there is 32bit padding between next_off and arg1:
(gdb) x/16xb scan
0x7f4978: 0xde 0x2d 0x02 0x00 0x00 0x00 0x00 0x00
0x7f4980: 0x00 0x11 0x0d 0x00 0x00 0x00 0x00 0x00
(gdb) print/x n
$1 = 0xd1100
This is suspicious. There's pointer and type conversion going on in perlbench, so perhaps type size assumptions fail somewhere. Compiling with multilib yields a working benchmark and examining the memory verifies that there is no padding.
Forcing the structure into a bitfield fixes the crash when performing a 64-bit compile:
struct regnode_1 {
U8 flags : 8;
U8 type : 8;
U16 next_off : 16;
U32 arg1 : 32;
};
This is how our little investigation progressed:
At first we thought it was some padding issue, but as Peter pointed out on Godbolt, no such thing occurs. So, the packing or not of the structure did not change anything.
Then, I got suspicious of the (clearly twisted) way that Perl handles pointers. The majority of the casts are violating strict aliasing as defined by the standard. Since the segmentation fault happened on a pointer cast, namely:
struct regnode {
U8 flags;
U8 type;
U16 next_off;
};
to
struct regnode_1 {
U8 flags;
U8 type;
U16 next_off;
U32 arg1;
};
However, enabling it with the -fstrict-aliasing flags didn't change anything. Although it qualifies as undefined behaviour, there is no overlap in memory, since the elements/nodes of the regular expression that is being currently parsed are laid out separately in memory.
Going deeper and checking the LLVM IR for the switch block in question, I got this in regexec.ll
; truncated
%876 = load %struct.regnode*, %struct.regnode** %scan, align 8, !dbg !8005
%877 = bitcast %struct.regnode* %876 to %struct.regnode_1*, !dbg !8005
%arg11715 = getelementptr inbounds %struct.regnode_1, %struct.regnode_1* %877, i32 0, i32 3, !dbg !8005
%878 = load i64, i64* %arg11715, align 8, !dbg !8005
store i64 %878, i64* %n, align 8, !dbg !8006
; truncated
The load/store instructions are using a 64-bit integer, which means that the pointer in C is interpreted as pointing to an 8 bytes integer (instead of 4). Thus, gathering 2 bytes outside the current regex node struct bounds for calculating the value of arg1. This value is in turn used as an array index which ultimately causes a segfault crash when it is out of array bounds.
Back to tracing where U32 is interpreted as a 64-bit unsigned integer. Looking into file spec_config.h, the conditional compilation leads (at least in my machine) to a preprocessor block that starts with
#elif !defined(SPEC_CPU_GOOFY_DATAMODEL)
which, according to a code comment in the surrounding area, is supposed to correspond to a ILP32 data model (see also this). However, U32TYPE is defined as an unsigned long, which on my machine is 64 bits.
So, the fix is to change the definition to
#define U32TYPE uint32_t
which, as stated in this, is guaranteed to be exactly 32 bits (if supported).
I'd like to complement the other answers by saying that it was enough for us to add -DSPEC_CPU_LP64 to work around the segfault (-DSPEC_LP64 in CPU2017). Would be nice if the SPEC group would add this to their FAQ. This also seems to apply to gcc, cactusADM, povray and wrf.
We have a python script generating the config files for us, I'll talk to people and see if I can share what we have so far to get it running for our compiler.
Edit: Seems to be accesible from the outside anyway, so here you go: spec.py

Kernel Dev: Setting ES:DI in real mode

I'm working on a toy kernel for fun and education (not a class project). I'm starting work on my memory manager, so I'm trying to get the memory map from BIOS using an INT 0x15, EAX=E820 call while still in Real Mode. I'm adapting my function from the osdev wiki (here, in the section "Getting an E820 Memory Map"). However, I want this to be a function I can call from my C code, so I'm trying to change it a bit. I want it to take two arguments: a pointer to where to store the map entries, and a pointer to an integer which will be incremented by the number of entries in the table.
According to the wiki, ES:DI needs to be pointing at where the data should be stored, so I split my first argument into two (the segment selector, pointer_to_map / 16, and the offset, pointer_to_map % 16). Here's part of C code:
typedef struct SMAP_entry {
unsigned int baseL; // Base address, a QWORD
unsigned int baseH;
unsigned int lengthL; // Length, a QWORD
unsigned int lengthH;
unsigned int type; // entry type
unsigned int ACPI; // extra data from ACPI 3.0
} SMAP_entry_t;
SMAP_entry_t data[100];
kprint("Pointer: ");
kprint_int((int) data, 16);
kprint_newline();
int res = 0;
read_mem_map(((int) data) / 16, ((int) data) % 16, &res);
kprint("res: ");
kprint_int(res, 16);
kprint_newline();
Here's part of my ASM code:
; performs a INT 0x15, eax=0xE820 call to find the memory map
; inputs: the pointer to the data table / 16, the pointer % 16, a pointer to an dword (int) which will be
; incremented by the number of entries after this function returns.
; preserves: no registers except esi
read_mem_map:
mov es, [esp + 4] ; set es to the value of the first argument
mov di, [esp + 8] ; set di to the value of the second argument
That's all I'm pasting in because the program triple-faults and shuts down the VM there. By moving ret commands around, I found that the function crashes on the very first line. If I comment out the call in C, then everything works as you'd expect.
I've read through Google that there's almost never a reason to set ES:DI directly, and in the code that I've found which does, they set it to a literal. How should I set ES:DI and if I shouldn't set it directly, how should I make the C and ASM interact in the correct way?
Each of the segment registers (on 80x86) have a visible part, and several hidden fields (the segment base, the segment limit and the segment's attributes - read/write, privilege level, etc).
In protected mode; when you load a segment register the CPU uses the visible part as an index into either the GDT or LDT, and loads the segment's hidden fields from that descriptor (in the GDT or LDT).
In real mode; the CPU does something completely different - it only sets the segment base to "visible part * 16" and doesn't use any (GDT, LDT) table.
Given the fact that you're using a 32-bit pointer to the data table and a 32-bit stack pointer (e.g. mov es, [esp + 4]); I assume your C code is in 32-bit protected mode. This is completely incompatible with real mode, partly because segment loads work completely differently and partly because the default operand/address size is 32-bit and not 16-bit.
All BIOS functions are designed for real mode. They can't be used in protected mode.
Basically; I'd recommend:
pass the pointer to the data table to your assembly as a 32-bit integer/pointer (and not 2 separate 16-bit integers)
call a "go to real mode" function (which will be slightly tricky, as you'd also be switching from a 32-bit stack to a 16-bit stack and will need a "32-bit return instruction" in 16-bit code).
split the pointer to the data table into its segment and offset in assembly, and load the segment (which should work correctly as you're in real mode now)
call the BIOS function (which should work correctly as you're in real mode now)
call a "go to protected mode" function (which will be slightly tricky again, including a "16-bit return instruction" in 32-bit code).
return to the (32-bit protected mode) caller
Instructions for switching from real mode to protected mode, and switching from protected mode to real mode, are included in Intel's system programmer's guide. :)

fread of a struct diffrent under solaris and linux

I'm reading in the first Bytes of an File with fread:
fread(&example_struct, sizeof(example_struct), 1, fp_input);
Which ends up with different results under linux and solaris? Whereby the example_struct (Elf32_Ehdr) is part of Standart GNU C Liborary defined in elf.h? I would be happy to know why this happens?
General the struct looks the following:
typedef struct
{
unsigned char e_ident[LENGTH];
TYPE_Half e_type;
} example_struct;
The Debugcode:
for(i=0;paul<sizeof(example_struct);i++){
printf("example_struct->e_ident[%i]:(%x) \n",i,example_struct.e_ident[i]);
}
printf("example_struct->e_type: (%x) \n",example_struct.e_type);
printf("example_struct->e_machine: (%x) \n",example_struct.e_machine);
Solaris output:
Elf32_Ehead->e_ident[0]: (7f)
Elf32_Ehead->e_ident[1]: (45)
...
Elf32_Ehead->e_ident[16]: (2)
Elf32_Ehead->e_ident[17]: (0)
...
Elf32_Ehead->e_type: (200)
Elf32_Ehead->e_machine: (6900)
Linux output:
Elf32_Ehead->e_ident[0]: (7f)
Elf32_Ehead->e_ident[1]: (45)
...
Elf32_Ehead->e_ident[16]: (2)
Elf32_Ehead->e_ident[17]: (0)
...
Elf32_Ehead->e_type: (2)
Elf32_Ehead->e_machine: (69)
Maybe similar to: http://forums.devarticles.com/c-c-help-52/file-io-linux-and-solaris-108308.html
You don't mention what CPU you have in the machines, maybe Sparc64 in the Solaris machine and x86_64 in the Linux box, but I would guess that you're having an endianness issue. Intel, ARM and most other common architectures today are what is known as little-endian, the Sparc architecture is big-endian.
Let's assume we have the value 0x1234 in a CPU register and we want to store it in memory (or on hard drive, it doesn't matter where). Let N be the memory address we want to write to. We will need to store this 16 bit integer as two bytes in memory, here comes the confusing part:
Using a big-endian machine will store 0x12 at address N and 0x34 at address N+1.
A little-endian machine will store 0x34 at address N and 0x12 at address N+1.
If we store a value using a little endian machine and read it back using a big endian machine we will have swapped the two bytes around and you'll get the issue that you are seeing.
Probably because of differences in the structure packing between the two platforms. It's a bad idea to read structures directly (as units) from external media, since issues like these tend to pop up.

store itdr on x64

I tried to get idt address in my driver, I made function in asm which returns what idtr contains:
.data
myData dq 0
.code
Function PROC
sidt myData
mov rax, myData
ret
Function ENDP
END
But the address which I get is weird, for example in windbg:
r idtr
idtr=fffff80000b95080
However my driver shows:
idtr = f80000b950800fff
I read that on x64 IDTR contains 64-bit base address of IDT table. I would appreciate if anyone explain why my output is not the same as from WinDbg.
This is what the Intel docs say about the SIDT instruction:
In 64-bit mode, the operand size is fixed at 8+2 bytes. The instruction stores 8-byte base and 2-byte limit values.
and:
DEST[0:15] <- IDTR(Limit);
DEST[16:79] <- IDTR(Base);
This means your myData variable needs to be 10 bytes long, and the instructions stores the limit in the first 2 bytes and base address in the next 8 bytes. This also explains why your value matches with WinDbg's value after the first ffff bytes.

Resources