The hidden __result local variable in armcc DWARF debug information - arm

I'm writing tools for debugging Cortex-M and I have discovered an artefact when reviewing the DWARF .debug_info section which the armcc outputs for some C source. (The exact compiler is ARM Compiler 5.05.)
For example when the C source contains a simple function such as:
int function(int a)
{
int x;
int y;
I have discovered that the .debug_info describes the x and y local variables as expected, and additionally a "hidden variable" called __result as follows:
<2><218>: Abbrev Number: 94 (DW_TAG_variable)
<219> DW_AT_name : __result
<222> DW_AT_type : DW_FORM_ref2 <0x188>
<225> DW_AT_location : 1 byte block: 50 (DW_OP_reg0 (r0))
<227> DW_AT_start_scope : 64
<228> DW_AT_artificial : 1
The clue of the "hidden" nature of this "variable", being the presence of the DW_AT_artificial flag.
I've have read the DWARF documentation regarding the DW_AT_artificial flag, which confirmed by suspicions. I've also deduced by experimentation that this feature relates to return value which this function, since this "variable" does not appear in the DWARF for a void typed function.
What I cannot find is any confirmation of this entity's use as intended by the designers of the armcc toolchain. Can anybody elaborate on the meaning and usage of my discovery?

Related

[]A\A]A^A_ and ;*3$" in compiled C binary

I'm on an Ubuntu 18.04 laptop coding C with VSCode and compiling it with GNU's gcc.
I'm doing some basic engineering on my own C code and I noticed a few interesting details, on of which is the pair []A\A]A^A_ and ;*3$" that seems to appear in every one of my compiled C binaries. Between them is usually (or always) strings that I hard code in for printf() functions.
An example is this short piece of code here:
#include <stdio.h>
#include <stdbool.h>
int f(int i);
int main()
{
int x = 5;
int o = f(x);
printf("The factorial of %d is: %d\n", x, o);
return 0;
}
int f(int i)
{
if(i == 0)
{
return i;
}
else
{
return i*f(i-1);
}
}
... is then compiled using gcc test.c -o test.
When I run strings test, the following is outputted:
/lib64/ld-linux-x86-64.so.2
0HSn(
libc.so.6
printf
__cxa_finalize
__libc_start_main
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
AWAVI
AUATL
[]A\A]A^A_
The factorial of %d is: %d
;*3$"
GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7697
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
test.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
_edata
printf##GLIBC_2.2.5
__libc_start_main##GLIBC_2.2.5
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize##GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
Same as other scripts I've written, the 2 pieces []A\A]A^A_ and ;*3$" always pop up, 1 before the strings used with printf and one right after.
I'm curious: What exactly do those strings mean? I'm guessing they mainly mark the begining and endding of the use of hard-coded output strings.
Our digital computers work on bits, most commonly clustered in bytes containing 8 bits each. The meaning of such a combination depends on the context and the interpretation.
A non-exhausting list of possible interpretation is:
ASCII characters with the eighth bit ignored or accepted only if 0;
signed or unsigned 8-bit integer;
operation code (or part of it) of one specific machine language, each processor (family) has its own different set.
For example, the hex value 0x43 can be seen as:
ASCII character 'C';
Unsigned 8-bit integer 67 (signed is the same if 2's complement is used);
Operation code "LD B,E" for a Z80 CPU (see, I'm really old and learned that processor in depth);
Operation code "EORS ari" for an ARM CPU.
Now strings simply (not to say "primitively") scans through the given file and tries so interpret the bytes as sequences of printable ASCII characters. By default a sequence has to have at least 4 characters and the bytes are interpreted as 7-bit ASCII. BTW, the file does not have to be an executable. You can scan any file but if you give it an object file by default it scans only sections that are loaded in memory.
So what you see are sequences of bytes which by chance are at least 4 printable characters in a row. And because some patterns are always in an executable it just looks as if they have a special meaning. Actually they have but they don't have to relate to your program's strings.
You can use strings to quickly peek into a file to find, well, strings which might help you with whatever you're trying to accomplish.
What you're seeing is an ASCII representation of a particular bit pattern that happens to be common in executable programs generated by that particular compiler. The pattern might correspond to a particular sequence of machine language instructions which the compiler is fond of emitting. Or it might correspond to a particular data structure which the compiler or linker uses to mark the various other pieces of data stored in the executable.
Given enough work, it would probably be possible to work out the actual details, for your C code and your particular version of your particular compiler, precisely what the bit patterns behind []A\A]A^A_ and ;*3$" correspond to. But I don't do much machine-language programming any more, so I'm not going to try, and the answers probably wouldn't be too interesting in the end, anyway.
But it reminds me of little quirk which I have noticed and can explain. Suppose you wrote the very simple program
int i = 12345;
If you compiled that program and ran strings on it, and if you told it to look for strings as short as two characters, you'd probably see (among lots of other short, meaningless strings), the string
90
and that bit pattern would, in fact, correspond to your variable! What's up with that?
Well, 12345 in hexadecimal is 0x3039, and most machines these days are little-endian, so those two bytes in memory are stored in the other order as
39 30
and in ASCII, 0x39 is '9', while 0x30 is '0'.
And if this is interesting to you, you can try compiling the program fragment
int i = 12345;
long int a = 1936287860;
long int b = 1629516649;
long int c = 1953719668;
long long int x = 48857072035144;
long long int y = 36715199885175;
and running strings -2 on it, and see what else you get.

Why does GCC store global and static int differently?

Here is my C program with one static, two global, one local and one extern variable.
#include <stdio.h>
int gvar1;
int gvar2 = 12;
extern int evar = 1;
int main(void)
{
int lvar;
static int svar = 4;
lvar = 2;
gvar1 = 3;
printf ("global1-%d global2-%d local+1-%d static-%d extern-%d\n", gvar1, gvar2, (lvar+1), svar, evar);
return 0;
}
Note that gvar1, gvar2, evar, lvar and svar are all defined as integers.
I disassembled the code using objdump and the debug_str for this shows as below:
Contents of section .debug_str:
0000 76617269 61626c65 732e6300 6c6f6e67 variables.c.long
0010 20756e73 69676e65 6420696e 74002f75 unsigned int./u
0020 73657273 2f686f6d 6534302f 72616f70 sers/home40/raop
0030 2f626b75 702f6578 616d706c 65730075 /bkup/examples.u
0040 6e736967 6e656420 63686172 00737661 nsigned char.sva
0050 72006d61 696e006c 6f6e6720 696e7400 r.main.long int.
0060 6c766172 0073686f 72742075 6e736967 lvar.short unsig
0070 6e656420 696e7400 67766172 31006776 ned int.gvar1.gv
0080 61723200 65766172 00474e55 20432034 ar2.evar.GNU C 4
0090 2e342e36 20323031 31303733 31202852 .4.6 20110731 (R
00a0 65642048 61742034 2e342e36 2d332900 ed Hat 4.4.6-3).
00b0 73686f72 7420696e 7400 short int.
Why is it showing the following?
unsigned char.svar
long int.lvar
short unsigned int.gvar1.gvar2.evar
How does GCC decide which type it should be stored as?
I am using GCC 4.4.6 20110731 (Red Hat 4.4.6-3)
Why is it showing the following?
Simple answer: It is not showing what you think but it is showing:
1 "variables.c"
2 "long unsigned int"
2a "unsigned int"
2b "int"
3 "/users/home40/raop/bkup/examples"
4 "unsigned char"
4a "char"
5 "svar"
6 "main"
7 "long int"
8 "lvar"
9 "short unsigned int"
10 "gvar1"
11 "gvar2"
12 "evar"
13 "GNU C 4.4.6 20110731 (Red Hat 4.4.6-3)"
14 "short int"
The section is named .debug_str; it contains a list of strings which are separated by NUL bytes. These strings are in any order and they are referenced by the section .debug_info. So the fact that svar is following unsigned char has no meaning at all.
The .debug_info section contains the actual debugging information. This section does not contain strings. Instead it will contain information like this:
...
Item 123:
Type of information: Data type
Name: 2b /* String #2b in ".debug_str" is "int" */
Kind of data type: Signed integer
Number of bits: 32
... some more information ...
Item 124:
Type of information: Global variable
Name: 8 /* "lvar" */
Data type defined by: Item 123
Stored at: Address 0x1234
... some more information ...
Begin item 125:
Type of information: Function
Name: 6 /* "main" */
... some more information ...
Item 126:
Type of information: Local variable
Name: 5 /* "svar" */
Data type defined by: Item 123
Stored at: Address 0x1238
... some more information ...
End item 125 /* Function "main" */
Item 127:
...
You can see this information using the following command:
readelf --debug-dump filename.o
Why does GCC store global and static int differently?
I compiled your example twice: Once with optimization and once without optimization.
Without optimization svar and gvar1 were stored exactly the same way: Data type int, stored on a fixed address. lvar was: Data type int, stored on the stack.
With optimization lvar and svar were stored the same way: Data type: int, not stored at all, instead they are treated as constant value.
(This makes sense because the values of these variables never change.)
The C11 specification (read n1570) -or older C standards- does not define at what addresses or offsets are stored global or static variables, so the implementation (your gcc compiler and your ld linker) is free to put them at any place.
The organization and layout of the data segments is an implementation detail.
You may want to read more about DWARF to understand debug information, which is useful to the gdb debugger.
You may want to read more about linkers and loaders, and about the ELF format, if you want to understand how they are working. On Linux, there are several utilities to inspect elf(5) files, including objdump(1), readelf(1), nm(1).
Notice that your GCC4.4 is an obsolete and old version of GCC. Current version is GCC7, and GCC8 will be released in a few weeks (spring 2018). I strongly recommend to upgrade your compiler.
If you need to understand how and why the data segments are organized in such way and why your implementation chooses such a layout, you could take advantage that both gcc and ld (from binutils) are free software, and study their source in details. You'll need many years of work, since they are complex software (more than ten million lines of source code).
If you happen to start studying the internals of GCC, be sure to study a recent version. Most people of the GCC community have probably forgotten the details of GCC4.4 (released in 2009). A lot of things have changed in GCC since that ancient thing. A few years ago, I have written many slides about GCC internals, see the documentation of GCC MELT.
BTW, the layout of data segments, or of variables inside them, might vary with optimization options. It might happen that lvar does not sit in memory (e.g. stays in a register only); it could happen that a static variable is removed (using something like the as-if rule) etc.
For a single translation unit foo.c, you might compile it into assembler code using gcc -fverbose-asm -S -O foo.c and look into the emitted foo.s assembler code.
To understand more how your ld linker work, you might look into some relevant linker script. You could find how ld is invoked from gcc by using gcc -v (instead of gcc) in your compilation and linking command.
In most cases, you should not care about the particular offsets (in object files or executables) or addresses (in the virtual address space of your process) of global or static variables. Be also aware of ASLR. The proc(5) filesystem can be used to understand your process.
(your question is severely lacking some motivation and context)

Ambiguous behaviour of .bss segment in C program

I wrote the simple C program (test.c) below:-
#include<stdio.h>
int main()
{
return 0;
}
and executed the follwing to understand size changes in .bss segment.
gcc test.c -o test
size test
The output came out as:-
text data bss dec hex filename
1115 552 8 1675 68b test
I didn't declare anything globally or of static scope. So please explain why the bss segment size is of 8 bytes.
I made the following change:-
#include<stdio.h>
int x; //declared global variable
int main()
{
return 0;
}
But to my surprise, the output was same as previous:-
text data bss dec hex filename
1115 552 8 1675 68b test
Please explain.
I then initialized the global:-
#include<stdio.h>
int x=67; //initialized global variable
int main()
{
return 0;
}
The data segment size increased as expected, but I didn't expect the size of bss segment to reduce to 4 (on the contrary to 8 when nothing was declared). Please explain.
text data bss dec hex filename
1115 556 4 1675 68b test
I also tried the comands objdump, and nm, but they too showed variable x occupying .bss (in 2nd case). However, no change in bss size is shown upon size command.
I followed the procedure according to:
http://codingfox.com/10-7-memory-segments-code-data-bss/
where the outputs are coming perfectly as expected.
When you compile a simple main program you are also linking startup code.
This code is responsible, among other things, to init bss.
That code is the code that "uses" 8 bytes you are seeing in .bss section.
You can strip that code using -nostartfiles gcc option:
-nostartfiles
Do not use the standard system startup files when linking. The standard system libraries are used normally, unless -nostdlib or -nodefaultlibs is used
To make a test use the following code
#include<stdio.h>
int _start()
{
return 0;
}
and compile it with
gcc -nostartfiles test.c
Youll see .bss set to 0
text data bss dec hex filename
206 224 0 430 1ae test
Your first two snippets are identical since you aren't using the variable x.
Try this
#include<stdio.h>
volatile int x;
int main()
{
x = 1;
return 0;
}
and you should see a change in .bss size.
Please note that those 4/8 bytes are something inside the start-up code. What it is and why it varies in size isn't possible to tell without digging into all the details of mentioned start-up code.

AVR GCC, assembly C stub functions, eor and the required constant value

I'm having this code:
uint16_t swap_bytes(uint16_t x)
{
asm volatile(
"eor, %A0, %B0" "\n\t"
"eor, %B0, %A0" "\n\t"
"eor, %A0, %B0" "\n\t"
: "=r" (x)
: "0" (x)
);
return x;
}
Which translates (by avr-gcc version 4.8.1 with -std=gnu99 -save-temps) to:
.global swap_bytes
.type swap_bytes, #function
swap_bytes:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
/* #APP */
; 43 "..\lib\own\ownlib.c" 1
eor, r24, r25
eor, r25, r24
eor, r24, r25
; 0 "" 2
/* #NOAPP */
ret
.size swap_bytes, .-swap_bytes
But then the compiler is complaining like that:
|65|Error: constant value required|
|65|Error: garbage at end of line|
|66|Error: constant value required|
|66|Error: garbage at end of line|
|67|Error: constant value required|
|67|Error: garbage at end of line|
||=== Build failed: 6 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
The mentioned lines are the ones with the eor commands. Why does the compiler having problems with that? The registers are even upper (>= r16) ones where nearly all operations are possible. constant value required sounds to me like it expects a literal... I dont get it.
Just to clarify for future googlers:
eor, r24, r25
has an extra comma after the eor. This should be written as:
eor r24, r25
I would also encourage you (again) to consider using gcc's __builtin_bswap16. In case you are not familiar with the gcc 'builtin' functions, they are functions that are built into the compiler, and (despite looking like functions) are typically inlined. They have been written and optimized by people who understand all the ins and outs of the various processors and can take into account things you may not have considered.
I understand the desire to keep code as small as possible. And I accept that it is possible that (somehow) this builtin on your specific processor is producing sub-optimal code (I assume you have checked?). On the other hand, it may produce exactly the same code. Or it may use some even more clever trick to do this. Or it might interleave instructions from the surrounding code to take advantage of pipelining (or some other avr-specific thing that I have never heard of because I don't speak 'avr').
What's more, consider this code:
int main()
{
return __builtin_bswap16(12345);
}
Your code always takes 3 instructions to process a swap. However with builtins, the compiler can recognize that the arg is constant and compute the value at compile time instead of at run time. Hard to be more efficient than that.
I could also point out the benefits of "easier to support." Writing inline asm is HARD to do correctly. And future maintainers hate to touch it cuz they're never quite sure how it works. And of course, the builtin is going to be more cross-platform portable.
Still not convinced? My last pitch: Even after you fix the commas, your inline asm code still isn't quite right. Consider this code:
int main(int argc, char *argv[])
{
return swap_bytes(argc) + swap_bytes(argc);
}
Because of the way you have written written swap_bytes (ie using volatile), gcc must compute the value twice (see the definition of volatile). Had you omitted volatile (or if you had used the builtin which does this correctly), it would have realized that argc doesn't change and re-used the output from the first call. Did I mention that correctly writing inline asm is HARD?
I don't know your code, constraints, level of expertise or requirements. Maybe your solution really is the best. The most I can do is to encourage you to think long and hard before using inline asm in production code.

Location of global variables with DWARF (and relocation)

When dynamically linking a binary with libraries, relocation information is used to bind the variables/functions of the different ELF objects. However DWARF is not affected by relocation: how is a debugger supposed to resolve global variables?
Let's say I have liba.so (a.c) defining a global variable (using GNU/Linux with GCC or Clang):
#include <stdio.h>
int foo = 10;
int test(void) {
printf("&foo=%p\n", &foo);
}
and an program b linked against liba.so (b.c):
#include <stdio.h>
extern int foo;
int main(int argc, char** argv) {
test();
printf("&foo=%p\n", &foo);
return 0;
}
I expect that "foo" will be instanciated in liba.so
but in fact it is instanciated in both liba.so and b:
$ ./b
&foo=0x600c68 # <- b .bss
&foo=0x600c68 # <- b .bss
The foo variable which is used (both by b and by lib.so) is in the .bss of b
and not in liba.so:
[...]
0x0000000000600c68 - 0x0000000000600c70 is .bss
[...]
0x00007ffff7dda9c8 - 0x00007ffff7dda9d4 is .data in /home/foo/bar/liba.so
0x00007ffff7dda9d4 - 0x00007ffff7dda9d8 is .bss in /home/foo/bar/liba.so
The foo variable is instanciated twice:
once in liba.so (this instance is not used when linked with program b)
once in b (this instance is used instance of the other in b).
(I don't really understand why the variable is instanciated in the executable.)
There is only a declaration in b (as expected) in the DWARF informations:
$ readelf -wi b
[...]
<1><ca>: Abbrev Number: 9 (DW_TAG_variable)
<cb> DW_AT_name : foo
<cf> DW_AT_decl_file : 1
<d0> DW_AT_decl_line : 3
<d1> DW_AT_type : <0x57>
<d5> DW_AT_external : 1
<d5> DW_AT_declaration : 1
[...]
and a location is found in liba.so:
$ readelf -wi liba.so
[...]
<1><90>: Abbrev Number: 5 (DW_TAG_variable)
<91> DW_AT_name : foo
<95> DW_AT_decl_file : 1
<96> DW_AT_decl_line : 3
<97> DW_AT_type : <0x57>
<9b> DW_AT_external : 1
<9b> DW_AT_location : 9 bloc d'octets: 3 d0 9 20 0 0 0 0 0 (DW_OP_addr: 2009d0)
[...]
This address is the location of the (unsued) instance of foo in liba.so (.data).
I end up with 2 instances of the foo global variable (on in liba.so and one in b);
only the first one can be seen with DWARF;
only the secone one is used.
How is the debugger supposed to resolve the foo global variable?
I don't really understand why the variable is instanciated in the executable.
You can find the answer here.
How is the debugger supposed to resolve the foo global variable
The debugger reads symbol tables (in addition to debug info), and foo does get defined in both the main executable b, and in liba.so:
nm b | grep foo
0000000000600c68 B foo
(I read the Oracle doc provided by #Employed Russian.)
The global variable reinstanciation is done for non-PIC code in order to dereference the variable in a non-PIC way without patching the non-PIC code:
a copy of the variable is done for non-PIC code;
the variable is instanciated in the executable;
a copy relocation instruction is used to copy the data from the source shared objet at dynamic linking time;
the instance in the shared objet is not used (after the relocation copy has been done).
Copy relocation instructions:
$readelf -r b
Relocation section '.rela.dyn' at offset 0x638 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600c58 000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000600ca8 001200000005 R_X86_64_COPY 0000000000600ca8 foo + 0
For functions, the GOT+PLT technique is used the same way they are used in PIC code.

Resources