summaryrefslogtreecommitdiffstats
path: root/src/bplist.c
diff options
context:
space:
mode:
authorGravatar Julien BLACHE2010-04-18 15:17:58 +0200
committerGravatar Jonathan Beck2010-04-18 15:17:58 +0200
commit33b8a1281f6fdaa9602956ae20e629b58a2e31ae (patch)
tree230505465f1eebbdd25efb80db1e6b863fde7650 /src/bplist.c
parenteb618a0c5c7a3893fc18a8f1e5d39854aa25c597 (diff)
downloadlibplist-33b8a1281f6fdaa9602956ae20e629b58a2e31ae.tar.gz
libplist-33b8a1281f6fdaa9602956ae20e629b58a2e31ae.tar.bz2
Endianness, alignment and type-punning fixes for binary plist support
- endianness issues: on big endian machines, writing out only part of an integer was broken (get_needed_bytes(x) < sizeof(x)) -> shift integer before memcpy() on big endian machines - alignment issues: unaligned reads when loading binary plist. Leads to slow runtime performance (kernel trapping and fixing things up), SIGBUS (kernel not helping us out) -> introduce get_unaligned() and have the compiler generate the code needed for the unaligned access (note that there remains unaligned accesses that I haven't been able to track down - I've seen 2 of them with test #2) - type-punning issues: breaking strict aliasing rules can lead to unexpected results as the compiler takes full advantage of the aliasing while optimizing -> introduce the plist_uint_ptr union instead of casting pointers Tested on amd64, alpha and hppa.
Diffstat (limited to 'src/bplist.c')
-rw-r--r--src/bplist.c76
1 files changed, 63 insertions, 13 deletions
diff --git a/src/bplist.c b/src/bplist.c
index 5618c38..0e248f4 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -79,6 +79,24 @@ static void float_byte_convert(uint8_t * address, size_t size)
79#endif 79#endif
80} 80}
81 81
82union plist_uint_ptr
83{
84 void *src;
85 uint8_t *u8ptr;
86 uint16_t *u16ptr;
87 uint32_t *u32ptr;
88 uint64_t *u64ptr;
89};
90
91#define get_unaligned(ptr) \
92 ({ \
93 struct __attribute__((packed)) { \
94 typeof(*(ptr)) __v; \
95 } *__p = (void *) (ptr); \
96 __p->__v; \
97 })
98
99
82static void byte_convert(uint8_t * address, size_t size) 100static void byte_convert(uint8_t * address, size_t size)
83{ 101{
84#if G_BYTE_ORDER == G_LITTLE_ENDIAN 102#if G_BYTE_ORDER == G_LITTLE_ENDIAN
@@ -95,23 +113,36 @@ static void byte_convert(uint8_t * address, size_t size)
95#endif 113#endif
96} 114}
97 115
98static uint32_t uint24_from_be(char *buff) 116static uint32_t uint24_from_be(union plist_uint_ptr buf)
99{ 117{
118 union plist_uint_ptr tmp;
100 uint32_t ret = 0; 119 uint32_t ret = 0;
101 uint8_t *tmp = (uint8_t *) &ret; 120
102 memcpy(tmp + 1, buff, 3 * sizeof(char)); 121 tmp.src = &ret;
103 byte_convert(tmp, sizeof(uint32_t)); 122
123 memcpy(tmp.u8ptr + 1, buf.u8ptr, 3 * sizeof(char));
124
125 byte_convert(tmp.u8ptr, sizeof(uint32_t));
104 return ret; 126 return ret;
105} 127}
106 128
107#define UINT_TO_HOST(x, n) \ 129#define UINT_TO_HOST(x, n) \
108 (n == 8 ? GUINT64_FROM_BE( *(uint64_t *)(x) ) : \ 130 ({ \
109 (n == 4 ? GUINT32_FROM_BE( *(uint32_t *)(x) ) : \ 131 union plist_uint_ptr __up; \
110 (n == 3 ? uint24_from_be( (char*)x ) : \ 132 __up.src = x; \
111 (n == 2 ? GUINT16_FROM_BE( *(uint16_t *)(x) ) : \ 133 (n == 8 ? GUINT64_FROM_BE( get_unaligned(__up.u64ptr) ) : \
112 *(uint8_t *)(x) )))) 134 (n == 4 ? GUINT32_FROM_BE( get_unaligned(__up.u32ptr) ) : \
113 135 (n == 3 ? uint24_from_be( __up ) : \
114#define be64dec(x) GUINT64_FROM_BE( *(uint64_t*)(x) ) 136 (n == 2 ? GUINT16_FROM_BE( get_unaligned(__up.u16ptr) ) : \
137 *__up.u8ptr )))); \
138 })
139
140#define be64dec(x) \
141 ({ \
142 union plist_uint_ptr __up; \
143 __up.src = x; \
144 GUINT64_FROM_BE( get_unaligned(__up.u64ptr) ); \
145 })
115 146
116#define get_needed_bytes(x) \ 147#define get_needed_bytes(x) \
117 ( ((uint64_t)x) < (1ULL << 8) ? 1 : \ 148 ( ((uint64_t)x) < (1ULL << 8) ? 1 : \
@@ -646,6 +677,11 @@ static void write_int(GByteArray * bplist, uint64_t val)
646 //do not write 3bytes int node 677 //do not write 3bytes int node
647 if (size == 3) 678 if (size == 3)
648 size++; 679 size++;
680
681#if G_BYTE_ORDER == G_BIG_ENDIAN
682 val = val << ((sizeof(uint64_t) - size) * 8);
683#endif
684
649 buff = (uint8_t *) malloc(sizeof(uint8_t) + size); 685 buff = (uint8_t *) malloc(sizeof(uint8_t) + size);
650 buff[0] = BPLIST_UINT | Log2(size); 686 buff[0] = BPLIST_UINT | Log2(size);
651 memcpy(buff + 1, &val, size); 687 memcpy(buff + 1, &val, size);
@@ -656,7 +692,7 @@ static void write_int(GByteArray * bplist, uint64_t val)
656 692
657static void write_real(GByteArray * bplist, double val) 693static void write_real(GByteArray * bplist, double val)
658{ 694{
659 uint64_t size = get_real_bytes(*((uint64_t *) & val)); //cheat to know used space 695 uint64_t size = get_real_bytes(val); //cheat to know used space
660 uint8_t *buff = (uint8_t *) malloc(sizeof(uint8_t) + size); 696 uint8_t *buff = (uint8_t *) malloc(sizeof(uint8_t) + size);
661 buff[0] = BPLIST_REAL | Log2(size); 697 buff[0] = BPLIST_REAL | Log2(size);
662 if (size == sizeof(double)) 698 if (size == sizeof(double))
@@ -748,6 +784,9 @@ static void write_array(GByteArray * bplist, GNode * node, GHashTable * ref_tabl
748 for (i = 0, cur = node->children; cur && i < size; cur = cur->next, i++) 784 for (i = 0, cur = node->children; cur && i < size; cur = cur->next, i++)
749 { 785 {
750 idx = *(uint64_t *) (g_hash_table_lookup(ref_table, cur)); 786 idx = *(uint64_t *) (g_hash_table_lookup(ref_table, cur));
787#if G_BYTE_ORDER == G_BIG_ENDIAN
788 idx = idx << ((sizeof(uint64_t) - dict_param_size) * 8);
789#endif
751 memcpy(buff + i * dict_param_size, &idx, dict_param_size); 790 memcpy(buff + i * dict_param_size, &idx, dict_param_size);
752 byte_convert(buff + i * dict_param_size, dict_param_size); 791 byte_convert(buff + i * dict_param_size, dict_param_size);
753 } 792 }
@@ -783,10 +822,16 @@ static void write_dict(GByteArray * bplist, GNode * node, GHashTable * ref_table
783 for (i = 0, cur = node->children; cur && i < size; cur = cur->next->next, i++) 822 for (i = 0, cur = node->children; cur && i < size; cur = cur->next->next, i++)
784 { 823 {
785 idx1 = *(uint64_t *) (g_hash_table_lookup(ref_table, cur)); 824 idx1 = *(uint64_t *) (g_hash_table_lookup(ref_table, cur));
825#if G_BYTE_ORDER == G_BIG_ENDIAN
826 idx1 = idx1 << ((sizeof(uint64_t) - dict_param_size) * 8);
827#endif
786 memcpy(buff + i * dict_param_size, &idx1, dict_param_size); 828 memcpy(buff + i * dict_param_size, &idx1, dict_param_size);
787 byte_convert(buff + i * dict_param_size, dict_param_size); 829 byte_convert(buff + i * dict_param_size, dict_param_size);
788 830
789 idx2 = *(uint64_t *) (g_hash_table_lookup(ref_table, cur->next)); 831 idx2 = *(uint64_t *) (g_hash_table_lookup(ref_table, cur->next));
832#if G_BYTE_ORDER == G_BIG_ENDIAN
833 idx2 = idx2 << ((sizeof(uint64_t) - dict_param_size) * 8);
834#endif
790 memcpy(buff + (i + size) * dict_param_size, &idx2, dict_param_size); 835 memcpy(buff + (i + size) * dict_param_size, &idx2, dict_param_size);
791 byte_convert(buff + (i + size) * dict_param_size, dict_param_size); 836 byte_convert(buff + (i + size) * dict_param_size, dict_param_size);
792 } 837 }
@@ -916,7 +961,12 @@ void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
916 for (i = 0; i < num_objects; i++) 961 for (i = 0; i < num_objects; i++)
917 { 962 {
918 uint8_t *offsetbuff = (uint8_t *) malloc(offset_size); 963 uint8_t *offsetbuff = (uint8_t *) malloc(offset_size);
919 memcpy(offsetbuff, offsets + i, offset_size); 964
965#if G_BYTE_ORDER == G_BIG_ENDIAN
966 offsets[i] = offsets[i] << ((sizeof(uint64_t) - offset_size) * 8);
967#endif
968
969 memcpy(offsetbuff, &offsets[i], offset_size);
920 byte_convert(offsetbuff, offset_size); 970 byte_convert(offsetbuff, offset_size);
921 g_byte_array_append(bplist_buff, offsetbuff, offset_size); 971 g_byte_array_append(bplist_buff, offsetbuff, offset_size);
922 free(offsetbuff); 972 free(offsetbuff);