1 /* -*- mode: C; c-basic-offset: 3; -*- */
3 /*--------------------------------------------------------------------*/
4 /*--- Segment name management aspacemgr-segnames.c ---*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Valgrind, a dynamic binary instrumentation
11 Copyright (C) 2015-2017 Florian Krohm
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 The GNU General Public License is contained in the file COPYING.
31 /* Segment names are stored in a string table.
33 The string table is organised into slots of varying length. Slots are
34 adjacent and there are no holes between slots.
35 A slot consists of two parts:
37 (1) a fixed size overhead of length 4 bytes
38 (2) a variable size payload of up to 65535 bytes
40 The segment name is stored in the payload area. Therefore:
41 a segment name cannot be longer than 65535 bytes including the '\0'
42 terminator. This looks like a reasonable limitation.
46 | 4 bytes | max 65535 bytes |
47 +-----------------------------+-------------------------+
48 | overhead | payload |
49 +-----------------------------+-------------------------+
54 Each slot is uniquely identified by an index which points to the first
55 byte of the payload area. It is this value that is stored in seg->fnIdx.
56 Note, that this value is at least 4.
58 A slot either holds a string or it is free. The status of a slot is
59 identified by the leftmost bit in the overhead field, the so called F-bit.
60 F-bit == 1 means that slot is free; otherwise it is occupied and holds a
63 Slot containing a string (segment name):
66 +---+--------------+----------+-------------------------+
67 | 0 | refcount | slotsize | the string including \0 |
68 +---+--------------+----------+-------------------------+
71 -4 -2 +----- seg->fnIdx
73 Segment names are reference counted. 15 bits are available which allows
74 for up to 32767 references. If the string is referenced more than 32767
75 times, the reference count will be frozen and the slot can never
76 become free. I'm not unduly concerned.
77 Two bytes are reserved to hold the size of the slot. Well, it's actually
78 the size of the payload aread (i.e. the size of the slot minus the
79 overhead). Ah well -- the name sticks.
80 With two bytes to store the size, the payload area can be at most 65535
83 A free slot looks like this:
86 +---+-------------------------+----------+--------------+
87 | 1 | index of next free slot | slotsize | .. unused .. |
88 +---+-------------------------+----------+--------------+
93 Free slots are chained together in a singly linked list. An index of
94 zero indicates the end of the chain. Note that zero cannot conflict
95 with an index into the string table as the minimum index is at least
98 The typical way to traverse the segment names is:
100 for (ix = overhead; (size = get_slotsize(ix)) != 0; ix += size + overhead) {
107 Important detail: there is a sentinel at the end of the list, namely a
108 slot with a zero-sized payload area.
110 Whenever a new segment name needs to be stashed away, the list of free
111 slots is traversed and the first slot which is large enough is being taken
112 (first fit). There will be no splitting of slots, as that complicates
113 matters and without slot coalescing would lead to memory fragmentation.
114 So we leave it as is until a use case comes up that needs something better.
117 #include "pub_core_basics.h" // types
118 #include "priv_aspacemgr.h"
122 refcount_size
= sizeof(UShort
),
123 slotsize_size
= sizeof(UShort
),
124 overhead
= refcount_size
+ slotsize_size
,
125 max_refcount
= 0x7fff, // 2 bytes - F-bit
126 max_slotsize
= 0xffff, // 2 bytes
127 max_slotindex
= 0x7fffffff, // 4 bytes - F-bit
128 fbit_mask_value
= 0x80,
132 static const UInt fbit_mask
= fbit_mask_value
;
134 /* The old segname implementation allowed for 1000 names on Android and
135 6000 names on other platforms. Each name was allowed to be 1000 characters
136 long. That was very wasteful. */
137 #define VG_TABLE_SIZE 1000000
139 /* String table for segment names */
141 static HChar segnames
[VG_TABLE_SIZE
]; /* her majesty, the string table */
142 static SizeT segnames_used
= 0; /* number of bytes used */
143 static UInt num_segnames
= 0; /* number of names in string table */
144 static UInt num_slots
= 0; /* number of slots in string table */
145 static UInt freeslot_chain
= end_of_chain
;
150 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
151 return (segnames
[ix
- 4] & fbit_mask
) != 0;
155 put_slotindex(UInt ix
, UInt slotindex
)
157 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
159 aspacem_assert(slotindex
>= overhead
&& slotindex
<= segnames_used
);
161 slotindex
|= fbit_mask
<< 24;
162 segnames
[ix
- 1] = slotindex
& 0xFF; slotindex
>>= 8;
163 segnames
[ix
- 2] = slotindex
& 0xFF; slotindex
>>= 8;
164 segnames
[ix
- 3] = slotindex
& 0xFF; slotindex
>>= 8;
165 segnames
[ix
- 4] = slotindex
& 0xFF;
169 get_slotindex(UInt ix
)
171 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
172 aspacem_assert(is_freeslot(ix
));
174 // Avoid unexpected sign extension
175 const UChar
*unames
= (const UChar
*)segnames
;
178 slotindex
|= unames
[ix
- 4]; slotindex
<<= 8;
179 slotindex
|= unames
[ix
- 3]; slotindex
<<= 8;
180 slotindex
|= unames
[ix
- 2]; slotindex
<<= 8;
181 slotindex
|= unames
[ix
- 1];
183 return slotindex
& max_slotindex
; // removes the F-bit
187 put_slotsize(UInt ix
, UInt size
)
189 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
190 aspacem_assert(size
<= max_slotsize
);
191 segnames
[ix
- 1] = size
& 0xff;
192 segnames
[ix
- 2] = size
>> 8;
196 get_slotsize(UInt ix
)
198 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
200 // Avoid unexpected sign extension
201 const UChar
*unames
= (const UChar
*)segnames
;
203 return (unames
[ix
] << 8) | unames
[ix
+1];
205 return (unames
[ix
- 2] << 8) | unames
[ix
- 1];
209 put_refcount(UInt ix
, UInt rc
)
211 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
212 aspacem_assert(rc
<= max_refcount
);
213 // rc <= max_refcount ensures that the F-bit is zero
214 segnames
[ix
- 3] = rc
& 0xff;
215 segnames
[ix
- 4] = rc
>> 8;
219 get_refcount(UInt ix
)
221 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
222 // must not be a free slot
223 aspacem_assert(! is_freeslot(ix
));
225 // Avoid unexpected sign extension
226 const UChar
*unames
= (const UChar
*)segnames
;
227 return (unames
[ix
- 4] << 8) | unames
[ix
- 3];
231 inc_refcount(UInt ix
)
233 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
234 UInt rc
= get_refcount(ix
);
235 if (rc
!= max_refcount
)
236 put_refcount(ix
, rc
+ 1);
240 dec_refcount(UInt ix
)
242 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
243 UInt rc
= get_refcount(ix
);
244 aspacem_assert(rc
> 0);
245 if (rc
!= max_refcount
) {
248 put_refcount(ix
, rc
);
250 UInt size
= get_slotsize(ix
);
251 /* Chain this slot in the freelist */
252 put_slotindex(ix
, freeslot_chain
);
253 put_slotsize(ix
+ slotsize_size
, size
);
256 if (0) VG_(am_show_nsegments
)(0, "AFTER DECREASE rc -> 0");
262 put_sentinel(UInt ix
)
264 aspacem_assert(ix
>= overhead
&& ix
<= segnames_used
);
271 /* Searches the string table to find an index for the given name.
272 If none is found, an index is allocated and the name stored.
273 If running ouf of memory, return -1. */
275 ML_(am_allocate_segname
)(const HChar
*name
)
277 UInt len
, ix
, size
, next_freeslot
;
279 aspacem_assert(name
);
281 if (0) VG_(debugLog
)(0, "aspacem", "allocate_segname %s\n", name
);
283 len
= VG_(strlen
)(name
);
285 /* First see if we already have the name. */
286 for (ix
= overhead
; (size
= get_slotsize(ix
)) != 0; ix
+= size
+ overhead
) {
287 if (is_freeslot(ix
)) continue;
288 if (VG_(strcmp
)(name
, segnames
+ ix
) == 0) {
294 /* Is there a free slot in the string table from a previously "freed"
297 for (prev
= -1, ix
= freeslot_chain
; ix
!= end_of_chain
;
298 prev
= ix
, ix
= next_freeslot
) {
299 next_freeslot
= get_slotindex(ix
); // next in chain
300 size
= get_slotsize(ix
);
302 if (size
>= len
+ 1) {
303 /* Note, if the size of the slot is a lot larger than the length
304 of the string we're about to store in it, we could split the
305 slot into two. But that complicates matters and as we're not
306 doing any coalescing of adjacent free slots this could lead to
309 freeslot_chain
= next_freeslot
;
311 put_slotindex(prev
, next_freeslot
);
313 put_slotsize(ix
, size
);
314 VG_(strcpy
)(segnames
+ ix
, name
);
320 /* We need to add a new name. */
322 /* Note, that we need at least two bytes in the payload. The reason is
323 that the payload area will be used to store the size of the slot when
324 the slot is on the freelist. */
325 if (len
== 0) len
= 1;
327 /* Is there enough room in the string table? The OVERHEAD is for the
328 sentinel following the payload of new slot. */
329 SizeT need
= len
+ 1 + overhead
;
330 if (need
> (sizeof segnames
) - segnames_used
) {
340 put_slotsize(ix
, len
+ 1);
341 VG_(strcpy
)(segnames
+ ix
, name
);
342 segnames_used
+= need
;
344 /* Add sentinel at end of segment name list */
345 put_sentinel(segnames_used
);
350 /* Debugging output */
352 ML_(am_show_segnames
)(Int logLevel
, const HChar
*prefix
)
356 VG_(debugLog
)(logLevel
, "aspacem", "%u segment names in %u slots\n",
357 num_segnames
, num_slots
);
359 if (freeslot_chain
== end_of_chain
)
360 VG_(debugLog
)(logLevel
, "aspacem", "freelist is empty\n");
362 VG_(debugLog
)(logLevel
, "aspacem", "freelist begins at %u\n",
364 for (i
= 0, ix
= overhead
; (size
= get_slotsize(ix
)) != 0;
365 ix
+= size
+ overhead
, ++i
) {
367 VG_(debugLog
)(logLevel
, "aspacem",
368 "(%u,%u,0) [free slot: size=%u next=%u]\n", i
, ix
,
369 get_slotsize(ix
), get_slotindex(ix
));
371 VG_(debugLog
)(logLevel
, "aspacem",
372 "(%u,%u,%u) %s\n", i
, ix
, get_refcount(ix
),
377 /* Returns a sequence number for the fnIdx position in segnames.
378 Used in aspacemgr debug output to associate a segment with
381 ML_(am_segname_get_seqnr
)(Int fnIdx
)
386 if (fnIdx
== -1) return -1; // shortcut
388 for (ix
= overhead
; (size
= get_slotsize(ix
)) != 0; ix
+= size
+ overhead
) {
394 // We should always find the given index; something's busted
399 /* Initialise the string table for segment names. It contains an empty
400 string which is not referenced. */
402 ML_(am_segnames_init
)(void)
404 aspacem_assert(sizeof segnames
>= overhead
);
406 segnames_used
= overhead
;
407 put_sentinel(segnames_used
);
410 /* Increase reference count of segment name identified by IX. */
412 ML_(am_inc_refcount
)(Int ix
)
418 /* Decrease reference count of segment name identified by IX. */
420 ML_(am_dec_refcount
)(Int ix
)
427 ML_(am_sane_segname
)(Int ix
)
429 return ix
== -1 || (ix
>= overhead
&& ix
< segnames_used
);
433 ML_(am_get_segname
)(Int ix
)
435 return (ix
== -1) ? NULL
: segnames
+ ix
;
438 /*--------------------------------------------------------------------*/
440 /*--------------------------------------------------------------------*/