I am currently learning how to disassemble Mach-O Binary Files and I am trying to figure out how to understand a 'Symbol Table' (in load cmd LC_SYMTAB).
How Do I Read / Interpret A Symbol Table and its entries?
I am not 100% of this but it appears that the entries are 8 bytes each? (correct me if I'm wrong)
I know that a string table is a group of strings separated by null bytes but what is a Symbol Table and its purpose?
Thanks.
Straight from <mach-o/nlist.h>:
struct nlist {
union {
uint32_t n_strx; /* index into the string table */
} n_un;
uint8_t n_type; /* type flag, see below */
uint8_t n_sect; /* section number or NO_SECT */
int16_t n_desc; /* see <mach-o/stab.h> */
uint32_t n_value; /* value of this symbol (or stab offset) */
};
struct nlist_64 {
union {
uint32_t n_strx; /* index into the string table */
} n_un;
uint8_t n_type; /* type flag, see below */
uint8_t n_sect; /* section number or NO_SECT */
uint16_t n_desc; /* see <mach-o/stab.h> */
uint64_t n_value; /* value of this symbol (or stab offset) */
};
So no, that shouldn't be 8 bytes, but rather 12 bytes for 32-bit and 16 bytes for 64-bit binaries.
Related
I have an array of structs that I need to initialize at compile-time (no memset) to 0xFF. This array will be written as part of the program over erased flash. By setting it to 0xFF, it will remain erased after programming, and the app can use it as persistent storage. I've found two ways to do it, one ugly and one a workaround. I'm wondering if there's another way with syntax I haven't found yet. The ugly way is to use a nested initializer setting every field of the struct. However, it's error prone and a little ugly. My workaround is to allocate the struct as an array of bytes and then use a struct-typed pointer to access the data. Linear arrays of bytes are much easier to initialize to a non-zero value.
To aid anyone else doing the same thing, I'm including the gcc attributes used and the linker script portion.
Example struct:
struct BlData_t {
uint8_t version[3];
uint8_t reserved;
uint8_t markers[128];
struct AppData_t {
uint8_t version[3];
uint8_t reserved;
uint32_t crc;
} appInfo[512] __attribute__(( packed ));
} __attribute__(( packed ));
Initialize to 0xFF using the best way I know:
// Allocate the space as an array of bytes
// because it's a simpler syntax to
// initialize to 0xFF.
__attribute__(( section(".bootloader_data") ))
uint8_t bootloaderDataArray[sizeof(struct BlData_t)] = {
[0 ... sizeof(struct BlData_t) - 1] = 0xFF
};
// Use a correctly typed pointer set to the
// array of bytes for actual usage
struct BlData_t *bootloaderData = (struct BlData_t *)&bootloaderDataArray;
No initialization necessary because of (NOLOAD):
__attribute__(( section(".bootloader_data") ))
volatile const struct BLData_t bootloader_data;
Addition to linker script:
.bootloader_data (NOLOAD):
{
FILL(0xFF); /* Doesn't matter because (NOLOAD) */
. = ALIGN(512); /* start on a 512B page boundary */
__bootloader_data_start = .;
KEEP (*(.bootloader_data)) /* .bootloader_data sections */
KEEP (*(.bootloader_data*)) /* .bootloader_data* sections */
. = ALIGN(512); /* end on a 512B boundary to support
runtime erasure, if possible */
__bootloader_data_end = .;
__bootloader_data_size = ABSOLUTE(. - __bootloader_data_start);
} >FLASH
How to use the starting address, ending address and size in code:
extern uint32_t __bootloader_data_start;
extern uint32_t __bootloader_data_end;
extern uint32_t __bootloader_data_size;
uint32_t _bootloader_data_start = (uint32_t)&__bootloader_data_start;
uint32_t _bootloader_data_end = (uint32_t)&__bootloader_data_end;
uint32_t _bootloader_data_size = (uint32_t)&__bootloader_data_size;
Update:
It turns out that I was asking the wrong question. I didn't know about the (NOLOAD) linker section attribute which tells the program loader not to burn this section into flash. I accepted this answer to help others realize my mistake and possibly theirs. By not even programming the section, I don't have to worry about the initialization at all.
I've upvoted the union answers since they seem to be a good solution to the question I asked.
I would use a union of your struct together with an array of the correct size, then initialize the array member.
union {
struct BlData_t data;
uint8_t bytes[sizeof(struct BlData_t)];
} data_with_ff = {
.bytes = {
[0 ... sizeof(struct BlData_t) - 1] = 0xff
}
};
You can then access your struct as data_with_ff.data, defining a pointer or macro for convenience if you wish.
Try on godbolt
(Readers should note that the ... in a designated initializer is a GCC extension; since the question was already using this feature and is tagged gcc I assume that is fine here. If using a compiler that doesn't have it, I don't know another option besides .bytes = { 0xff, 0xff, 0xff ... } with the actual correct number of 0xffs; you'd probably want to generate it with a script.)
The sensible way to do this is to find the command in the linker script telling it to back off from touching that memory in the first place. Because why would you want it do be erased only to filled up with 0xFF again? That only causes unnecessary flash wear for nothing.
Something along the lines of this:
.bootloader_data (NOLOAD) :
{
. = ALIGN(512);
*(.bootloader_data *)
} >FLASH
If you truly need to do this initialization and in pure standard C, then you can wrap your inner struct inside an anonymous union (C11), then initialize that one using macro tricks:
struct BlData_t {
uint8_t version[3];
uint8_t reserved;
uint8_t markers[128];
union {
struct AppData_t {
uint8_t version[3];
uint8_t reserved;
uint32_t crc;
} appInfo[512];
uint8_t raw [512];
};
};
#define INIT_VAL 0xFF, // note the comma
#define INIT_1 INIT_VAL
#define INIT_2 INIT_1 INIT_1
#define INIT_5 INIT_2 INIT_2 INIT_1
#define INIT_10 INIT_5 INIT_5
/* ... you get the idea */
#define INIT_512 INIT_500 INIT_10 INIT_2
const struct BlData_t bld = { .raw = {INIT_512} };
This method could also be applied on whole struct basis, if you for example want to initialize a struct array with all items set to the same values at compile-time.
I am trying to design a data structure (I have made it much shorter to save space here but I think you get the idea) to be used for byte level communication:
/* PACKET.H */
#define CM_HEADER_SIZE 3
#define CM_DATA_SIZE 16
#define CM_FOOTER_SIZE 3
#define CM_PACKET_SIZE (CM_HEADER_SIZE + CM_DATA_SIZE + CM_FOOTER_SIZE)
// + some other definitions
typedef struct cm_header{
uint8_t PacketStart; //Start Indicator 0x5B [
uint8_t DeviceId; //ID Of the device which is sending
uint8_t PacketType;
} CM_Header;
typedef struct cm_footer {
uint16_t DataCrc; //CRC of the 'Data' part of CM_Packet
uint8_t PacketEnd; //should be 0X5D or ]
} CM_Footer;
//Here I am trying to conver a few u8[4] tp u32 (4*u32 = 16 byte, hence data size)
typedef struct cm_data {
union {
struct{
uint8_t Value_0_0:2;
uint8_t Value_0_1:2;
uint8_t Value_0_2:2;
uint8_t Value_0_3:2;
};
uint32_t Value_0;
};
//same thing for Value_1, 2 and 3
} CM_Data;
typedef struct cm_packet {
CM_Header Header;
CM_Data Data;
CM_Footer Footer;
} CM_Packet;
typedef struct cm_inittypedef{
uint8_t DeviceId;
CM_Packet Packet;
} CM_InitTypeDef;
typedef struct cm_appendresult{
uint8_t Result;
uint8_t Reason;
} CM_AppendResult;
extern CM_InitTypeDef cmHandler;
The goal here is to make reliable structure for transmitting data over USB interface. At the end the CM_Packet should be converted to an uint8_t array and be given to data transmit register of an mcu (stm32).
In the main.c file I try to init the structure as well as some other stuff related to this packet:
/* MAIN.C */
uint8_t packet[CM_PACKET_SIZE];
int main(void) {
//use the extern defined in packet.h to init the struct
cmHandler.DeviceId = 0x01; //assign device id
CM_Init(&cmHandler); //construct the handler
//rest of stuff
while(1) {
CM_GetPacket(&cmHandler, (uint8_t*)packet);
CDC_Transmit_FS(&packet, CM_PACKET_SIZE);
}
}
And here is the implementation of packet.h which screws up everything so bad. I added the packet[CM_PACKET_SIZE] to watch but it is like it is just being generated randomly. Sometimes by pure luck I can see in this array some of the values that I am interested in! but it is like 1% of the time!
/* PACKET.C */
CM_InitTypeDef cmHandler;
void CM_Init(CM_InitTypeDef *cm_initer) {
cmHandler.DeviceId = cm_initer->DeviceId;
static CM_Packet cmPacket;
cmPacket.Header.DeviceId = cm_initer->DeviceId;
cmPacket.Header.PacketStart = CM_START;
cmPacket.Footer.PacketEnd = CM_END;
cm_initer->Packet = cmPacket;
}
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier,
uint8_t *data){
CM_AppendResult result;
switch(identifier){
case CM_VALUE_0:
handler->Packet.Data.Value_0_0 = data[0];
handler->Packet.Data.Value_0_1 = data[1];
handler->Packet.Data.Value_0_2 = data[2];
handler->Packet.Data.Value_0_3 = data[3];
break;
//Also cases for CM_VALUE_0, 1 , 2
//to build up the CM_Data sturct of CM_Packet
default:
result.Result = CM_APPEND_FAILURE;
result.Reason = CM_APPEND_CASE_ERROR;
return result;
break;
}
result.Result = CM_APPEND_SUCCESS;
result.Reason = 0x00;
return result;
}
void CM_GetPacket(CM_InitTypeDef *handler, uint8_t *packet){
//copy the whole struct in the given buffer and later send it to USB host
memcpy(packet, &handler->Packet, sizeof(CM_PACKET_SIZE));
}
So, the problem is this code gives me 99% of the time random stuff. It never has the CM_START which is the start indicator of packet to the value I want to. But most of the time it has the CM_END byte correctly! I got really confused and cant find out the reason. Being working on an embedded platform which is hard to debugg I am kind of lost here...
If you transfer data to another (different) architecture, do not just pass a structure as a blob. That is the way to hell: endianess, alignment, padding bytes, etc. all can (and likely will) cause trouble.
Better serialize the struct in a conforming way, possily using some interpreted control stream so you do not have to write every field out manually. (But still use standard functions to generate that stream).
Some areas of potential or likely trouble:
CM_Footer: The second field might very well start at a 32 or 64 bit boundary, so the preceeding field will be followed by padding. Also, the end of that struct is very likely to be padded by at least 1 bytes on a 32 bit architecture to allow for proper alignment if used in an array (the compiler does not care you if you actually need this). It might even be 8 byte aligned.
CM_Header: Here you likely (not guaranteed) get one uint8_t with 4*2 bits with the ordering not standardized. The field my be followed by 3 unused bytes which are required for the uint32_t interprettion of the union.
How do you guarantee the same endianess (for >uint8_t: high byte first or low byte first?) for host and target?
In general, the structs/unions need not have the same layout for host and target. Even if the same compiler is used, their ABIs may differ, etc. Even if it is the same CPU, there might be other system constraints. Also, for some CPUs, different ABIs (application binary interface) exist.
Interoperating with C code, I was not able to directly cast a structure and I was forced to define an equivalent one in Go.
The C function from libproc.h is
int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize)
The C structure for flavor==PROC_PIDTASKINFO is proc_taskinfo as defined in sys/proc_info.h (included by libproc.h):
struct proc_taskinfo {
uint64_t pti_virtual_size; /* virtual memory size (bytes) */
uint64_t pti_resident_size; /* resident memory size (bytes) */
uint64_t pti_total_user; /* total time */
uint64_t pti_total_system;
uint64_t pti_threads_user; /* existing threads only */
uint64_t pti_threads_system;
int32_t pti_policy; /* default policy for new threads */
int32_t pti_faults; /* number of page faults */
int32_t pti_pageins; /* number of actual pageins */
int32_t pti_cow_faults; /* number of copy-on-write faults */
int32_t pti_messages_sent; /* number of messages sent */
int32_t pti_messages_received; /* number of messages received */
int32_t pti_syscalls_mach; /* number of mach system calls */
int32_t pti_syscalls_unix; /* number of unix system calls */
int32_t pti_csw; /* number of context switches */
int32_t pti_threadnum; /* number of threads in the task */
int32_t pti_numrunning; /* number of running threads */
int32_t pti_priority; /* task priority*/
};
Even if Go code actually works, I was not able to use C.proc_taskinfo directly. The Go function is propertiesOf(): complete source here.
If I reference the C structure, I got a similar error reported as in my latest question on the subject: could not determine kind of name for C.proc_taskinfo, but this time I'm sure the definition is imported with #include.
As per documentation
To access a struct, union, or enum type directly, prefix it with struct_, union_, or enum_, as in C.struct_stat.
Use C.struct_proc_taskinfo.
I'm struggling to write a function that translates virtual memory address to physical ones. I want the function to return the physical memory address of a virtual 16-bit address.
I'm just using 16-bit long virtual addresses that ignore permission bits (read,write, etc.). There are 256 pages in the page table so the base table register, which is BTR, just points to the table.
I want the function to either return:
Success: the physical address (a void*)
Page Fault: the virtual page number (an integer)
Protection Fault: a copy of the PTE
Here is the page table entry:
31 30..28 27..............................................4 3 2 1 0
+-----+------+-------------------------------------------------+-+-+-+-+
|Valid|unused| 24-bit Physical Page Number |P|R|W|X|
+-----+------+-------------------------------------------------+-+-+-+-+
I'm trying to learn how virtual memory works. I'm confused on how I would take a 16-bit virtual address and map it to a 32-bit page table entry, and then how to get the physical address from there. I've defined result_t and a union of the page table entry below, but I'm unsure how to use them. I've gotten a lot of help from looking online but everything is getting muddle together, I just want to learn how everything works straightforwardly.
Here are some necessary definitions:
extern void* BTR;
typedef struct result_st {
enum {SUCCESS, PAGEFAULT,
PROTFAULT, NOTIMPLEMENTED} status;
union {
void* pa;
unsigned vpn;
unsigned pte;
} value;
} result_t;
static result_t success(void* pa) {
result_t res;
res.status=SUCCESS;
res.value.pa = pa;
return res;
}
typedef union {
unsigned All;
struct {
unsigned Valid :1;
unsigned Unused :3;
unsigned PhysicalPageNumber :24;
unsigned SupervisoryMode :1;
unsigned Read :1;
unsigned Execute :1;
unsigned Write :1;
};
} PageTableEntry;
static int is_valid (unsigned pte) {
return 1 & (pte >> 31);
}
Here is the function I am writing:
result_t legacy(unsigned short va)
{
result_t result;
unsigned pte = va << 8;
result.value.pte = pte;
// This is my attempt so far.
// I want to use is_Valid() somewhere
void* pa = pte >> 8 | (va & 255);
return success(pa);
}
Thanks for any advice you can provide!
You are missing the definition of the actual page table. I assume it's like this (assuming I have understood your question correctly):
#define PAGE_TABLE_SIZE 256
PageTable page_table[PAGE_TABLE_SIZE];
Then your code would look something like this:
#define VIRT_PAGE_SIZE_BITS 8
/* Get virtual page number by dividing by the virt page size */
unsigned int virt_page_num = va >> VIRT_PAGE_SIZE_BITS;
assert(virt_page_num < PAGE_TABLE_SIZE); // Or do proper error handling
/* Use virtual page number to index into page table */
PageTableEntry pte = page_table[virt_page_num];
if (is_valid(pte)) {
if (is_access_ok(pte)) {
unsigned int phys_page_num = pte.PhysicalPageNumber;
return success(phys_page_num);
} else {
/* Protection fault code goes here */
}
} else {
/* Page fault code goes here */
}
I found one code implemented as the similar demo shown below ..
struct st
{
int a;
struct
{
int b;
};
};
6.58 Unnamed struct/union fields within structs/unions
As permitted by ISO C11.
But What are benefits of it ?
Because anyway I can access the data members in a same manner like
int main()
{
struct st s;
s.a=11;
s.b=22;
return 0;
}
compiled on gcc 4.5.2 with ,
gcc -Wall demo.c -o demo
and no errors ,
It does not have to be an anonymous struct inside a struct, which I do not find very useful: this will typically only change the layout slightly by introducing more padding, with no other visible effects (compared to inlining the members of the child struct into the parent struct).
I think that the advantage of anonymous struct/unions is elsewhere:
they can be used to place an anonymous struct inside an union or an anonymous union inside a struct.
Example:
union u
{
int i;
struct { char b1; char b2; char b3; char b4; };
};
The benefit is pretty obvious, isn't it? It saves the programmer from coming up with a name! Since naming things is hard, it's nice that it's possible to avoid doing so if there is no real need.
It's also a pretty clear signal that this struct is local and never used anywhere else but in the context of being a field in the parent struct, which is really, really nice information since it reduces the possibility of needless coupling.
Think of it as static; it restricts the visibility of the inner struct to the outer one, in a manner similar to (but not, of course, equivalent with) how static restricts the visibility of global symbols to the compilation unit in which they appear.
I just ran into a huge benefit of anonymous union. However be warned this is not a story for the faint hearted nor is it a recommended practice.
Note: See also Anonymous union within struct not in c99?
In an older C program of hundreds of source code files there is a global variable, a struct, which contained a struct as a member. So the type definition for the global variable looked some thing like:
typedef struct {
LONG lAmount;
STRUCTONE largeStruct; // memory area actually used for several different struct objects
ULONG ulFlags;
} STRUCTCOMMON;
The struct, STRUCTONE, was one of several large structs however the others were all smaller than STRUCTONE at the time this code was written. So this memory area, largeStruct was being used as a union but without the proper source statements indicating so. Instead various struct variables were copied into this area using memcpy(). To make matters worse sometimes this was through the actual name of the global variable and sometimes through a pointer to the global variable.
As typically happens as time progresses recent changes resulted in one of the other structs becoming the largest. And I was faced with having to go through a hundred files looking for where this was being used along with all the various aliases and everything else.
And then I remembered anonymous unions. So I modified the typedef to be the following:
typedef struct {
LONG lAmount;
union {
// anonymous union to allow for allocation of largest space needed
STRUCTONE largeStruct; // memory area actually used for several different struct objects
STRUCTTHREE largerStruct; // memory area for even larger struct
};
ULONG ulFlags;
} STRUCTCOMMON;
And then recompiled every thing.
So now all those days of source code review and regression testing I was unhappily looking forward to are no longer necessary.
And I can now begin the process of slowly modifying source using this global to bring this source up to more modern standards on my own time table.
Addendum - Anonymous struct within anonymous union
Working in this same source code body I ran into an application of this technique with a binary record that could contain date from one of several different structs which were supposed to be the same length. The problem I found was due to a programmer error, one struct was a different size than the others.
As part of correcting this problem, I wanted a solution that would allow the compiler to figure out the correct sizes for the data structures.
Since these structs contained some differences in a couple of members of the structs with padding variables added to make them all the same size, I went with anonymous unions which worked fine except for one of the structs.
I found I could add an anonymous struct as part of the union so that as long as the various members of the union and the added anonymous struct had different names, it would compile fine with Visual Studio 2015.
Important Note: This solution requires #pragma pack(1) with Visual Studio 2015 to pack the structs and unions on byte boundaries. Without the use of the pragma the compiler may introduce unknown padding into the various structs and unions.
I created the following define in order to standardize the anonymous union and anonymous struct.
#define PROGRPT_UNION_STRUCT \
union { \
SHORT sOperand1; /* operand 1 (SHORT) */ \
LONG lOperand1; /* operand 1 (LONG) */ \
PROGRPT_ITEM Operand1; /* operand 1 */ \
struct { \
UCHAR uchReserved3; /* */ \
USHORT usLoopEnd; /* offset for loop end */ \
UCHAR uchReserved4; /* */ \
}; \
};
Then used it as in this sample of three of the several structs that are used to access the binary data in the data record read from a file.
/* loop record */
typedef struct {
UCHAR uchOperation; /* operation code (LOOP) */
UCHAR uchRow; /* position (row) */
UCHAR uchLoopBrace; /* loop brace (begin/end) */
UCHAR uchReserved1; /* */
TCHAR auchReserved2[ 2 ]; /* */
UCHAR uchCondition; /* condition code */
PROGRPT_ITEM LoopItem; /* loop record */
PROGRPT_UNION_STRUCT
PROGRPT_ITEM Reserved5; /* */
} PROGRPT_LOOPREC;
/* print record */
typedef struct {
UCHAR uchOperation; /* operation code (PRINT) */
UCHAR uchRow; /* position (row) */
UCHAR uchColumn; /* position (column) */
UCHAR uchMaxColumn; /* max no of column */
TCHAR auchFormat[ 2 ]; /* print format/style */
UCHAR uchCondition; /* condition code */
PROGRPT_ITEM PrintItem; /* print item */
PROGRPT_UNION_STRUCT
PROGRPT_ITEM Operand2; /* ope2 for condition */
} PROGRPT_PRINTREC;
/* mathematics record ( accumulator.total = LONG (+,-,*,/) opr2) */
typedef struct {
UCHAR uchOperation; /* operation code (MATH) */
UCHAR uchRow; /* position (row) */
UCHAR uchColumn; /* position (column) */
UCHAR uchMaxColumn; /* max no of column */
TCHAR auchFormat[ 2 ]; /* format style */
UCHAR uchCondition; /* condition code */
PROGRPT_ITEM Accumulator; /* accumulator */
PROGRPT_UNION_STRUCT
PROGRPT_ITEM Operand2; /* operand 2 */
} PROGRPT_MATHTTL;
which were originally
typedef struct {
UCHAR uchOperation; /* operation code (LOOP) */
UCHAR uchRow; /* position (row) */
UCHAR uchLoopBrace; /* loop brace (begin/end) */
UCHAR uchReserved1; /* */
TCHAR auchReserved2[ 2 ]; /* */
UCHAR uchCondition; /* condition code */
PROGRPT_ITEM LoopItem; /* loop record */
UCHAR uchReserved3; /* */
USHORT usLoopEnd; /* offset for loop end */
UCHAR uchReserved4; /* */
PROGRPT_ITEM Reserved5; /* */
} PROGRPT_LOOPREC;
/* print record */
typedef struct {
UCHAR uchOperation; /* operation code (PRINT) */
UCHAR uchRow; /* position (row) */
UCHAR uchColumn; /* position (column) */
UCHAR uchMaxColumn; /* max no of column */
TCHAR auchFormat[ 2 ]; /* print format/style */
UCHAR uchCondition; /* condition code */
PROGRPT_ITEM PrintItem; /* print item */
PROGRPT_ITEM Operand1; /* ope1 for condition */
PROGRPT_ITEM Operand2; /* ope2 for condition */
} PROGRPT_PRINTREC;
/* mathematics record ( accumulator.total = LONG (+,-,*,/) opr2) */
typedef struct {
UCHAR uchOperation; /* operation code (MATH) */
UCHAR uchRow; /* position (row) */
UCHAR uchColumn; /* position (column) */
UCHAR uchMaxColumn; /* max no of column */
TCHAR auchFormat[ 2 ]; /* format style */
UCHAR uchCondition; /* condition code */
PROGRPT_ITEM Accumulator; /* accumulator */
LONG lOperand1; /* operand 1 (LONG) */
PROGRPT_ITEM Operand2; /* operand 2 */
} PROGRPT_MATHTTL;
Using a union of all the various record types that looks like:
typedef union {
PROGRPT_LOOPREC Loop; /* loop record */
PROGRPT_PRINTREC Print; /* print record */
PROGRPT_MATHOPE MathOpe; /* math (with operand) */
PROGRPT_MATHTTL MathTtl; /* math (with total) */
PROGRPT_MATHCO MathCo; /* math (with count) */
} PROGRPT_RECORD;
These record formats are used in the code similar to the following:
for ( usLoopIndex = 0; usLoopIndex < usMaxNoOfRec; ) {
ULONG ulActualRead = 0; /* actual length of read record function */
PROGRPT_RECORD auchRecord;
/* --- retrieve a formatted record --- */
ProgRpt_ReadFile( ulReadOffset, &auchRecord, PROGRPT_MAX_REC_LEN, &ulActualRead );
if ( ulActualRead != PROGRPT_MAX_REC_LEN ) {
return ( LDT_ERR_ADR );
}
/* --- analyze operation code of format record, and
store it to current row item buffer --- */
switch ( auchRecord.Loop.uchOperation ) {
case PROGRPT_OP_PRINT: /* print operation */
sRetCode = ProgRpt_FinPRINT( &ReportInfo, &auchRecord.Print, uchMinorClass, NULL );
break;
case PROGRPT_OP_MATH: /* mathematics operation */
sRetCode = ProgRpt_FinMATH(&auchRecord.MathOpe, NULL );
break;
case PROGRPT_OP_LOOP: /* loop (begin) operation */
ProgRpt_PrintLoopBegin( &ReportInfo, &auchRecord.Loop );
switch ( auchRecord.Loop.LoopItem.uchMajor ) {
case PROGRPT_INDKEY_TERMNO:
sRetCode = ProgRpt_IndLOOP( &ReportInfo, &auchRecord.Loop, uchMinorClass, usTerminalNo, ulReadOffset );
usLoopIndex += auchRecord.Loop.usLoopEnd;
ulReadOffset += ( PROGRPT_MAX_REC_LEN * auchRecord.Loop.usLoopEnd );
break;
default:
return ( LDT_ERR_ADR );
}
break;
default:
return ( LDT_ERR_ADR );
}
// .......
I've used anonymous structs in developing contiguous address structures that I'll be accessing via pointers. More specifically, I'll use anonymous structs within the parent struct to enable bit-fielding certain portions of the memory that is divided into smaller portions of labeled data.
Be careful of how your compiler packs the bit-fielded information, the first member of the bitfielded struct can either be the LSB or MSB.
typedef struct
{
uint32_t a;
struct
{
uint32_t b : 16;
uint32_t c : 8;
uint32_t d : 7;
uint32_t e : 1;
};
}Parent;
#define ADDRESS ((Parent*)(uint16_t)0xF0F0)
ADDRESS->a = data_32_bits;
ADDRESS->b = data_16_bits;
ADDRESS->c = data_8_bits;
ADDRESS->d = data_7_bits;
ADDRESS->e = data_1_bit;