0

Baremetal riscv gcc compiled code and linked. I can load my program at any address. firmware calls my program using pointer to my function. I am not able to print the global variables address, its printing offsets instead of absolute address. what to do? running on qemu. If I am giving fixed address to ram in memory map. I am getting correct address but without giving memory map I am getting offset as its actual absolute address. But thats not I wanted.

this is the below code

void fun() {
      __attribute__((used)) static const int lsc = 0x55; //.const
      printf("%d %x %x\n",lsc,lsc,&lsc);
}

when printing the address of local_static_const, I am getting some offset 0xd0, but not absolute address.

I randomly found out that -Wl,-mno-relax giving me correct output but if I am removing then again its giving me wrong output .i.e giving offset address. I am not able to understand why.

Note: %p is not the issue, I have changed it to %x, its working in the same way listed above.

shaik reyan
  • 129
  • 1
  • 8
  • 1
    What makes you think it is an offset? What is the actual output you get? What do you think is the offset relative to? How do you rule out that the address is in fact 0xd0 within the process-specific address space of your program? Are you aware of (oh, this part is covered by answers below now...) – Yunnosch Jul 03 '23 at 10:01
  • 1
    Show the exact text of observed output and the output desired instead. – Eric Postpischil Jul 03 '23 at 10:12
  • 1
    GCC should give some hint that the type of your argument (pointer) does not match the expected type (unsigned int) for your format specifier. You should pay attention to warnings from the compiler. If there was no warning, try to add options `-Wextra -Wall -pedantic` – Gerhardh Jul 03 '23 at 10:16
  • I am loading firmware at particular address like 0x50000000 and memory layout starts at this address and this function is compiled/linked seperately and loaded at address 0x60000000. so the address should not be 0xd0 It should be something like 0x500000d0. Correct me If I am wrong. – shaik reyan Jul 03 '23 at 10:18
  • I randomly found out that -Wl,-mno-relax giving me correct output but if I am removing then again its giving me wrong output .i.e giving offset address. – shaik reyan Jul 03 '23 at 10:20
  • Are you sure you didn't miss an extra `0` in your addresses? Having 64 bit CPU doesn't mean that integers are also 64 bits. You could also have 64 bit pointers and 32 bit `int` types. And 64 bit addresses obviously don't fit into 32 bit integers. – Gerhardh Jul 03 '23 at 10:44
  • It would be an interesting experiment to see what would happen if the data were a static local array containing a string, instead of a static local integer. For example: `fun() { static const char s[] = "hello"; printf("%s %x %p\n", s, (unsigned) s, (void *) s); }`. – John Bollinger Jul 05 '23 at 13:47

3 Answers3

2

The printf format for pointers is %p and the address must be cast as (void *). Try this:

void fun() {
      __attribute__((used)) static const int lsc = 0x55; //.const
      printf("%d %x %p\n", lsc, lsc, (void *)&lsc);
}

If the above code prints 0x000000d0 for the third argument, that is the address of the static variable lsc, which should be part of the data segment. You say the program is loaded at a fixed address: you might want to print the address of the function fun itself to see if the text or code segment is loaded as requested.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thanks, But My question is why its printing offset and not absolute address – shaik reyan Jul 03 '23 at 10:21
  • 2
    @shaikreyan: This answer is a possible solution to why your code appears to be printing an offset rather than an absolute address. Did you test it? – Eric Postpischil Jul 03 '23 at 10:56
  • @shaikreyan, nowhere in your question is it evident that you are asking *why* the behavior you describe occurs. The only explicit question is "what to do?", and the rest is consistent with a request for a fix, not for an explanation. Moreover, this answer in fact does provide both a fix (use `printf` correctly) and an explanation (you *didn't* use `printf` correctly). – John Bollinger Jul 03 '23 at 11:10
  • @JohnBollinger Yea I have tried it. – shaik reyan Jul 03 '23 at 11:45
  • @EricPostpischil Will Correct the question. Thanks. – shaik reyan Jul 03 '23 at 11:45
0

It seems the problem is that you are using an incorrect conversion specifier to output a pointer. The conversion specification %x is designed to output objects of the type unsigned int that usually occupy 4 bytes while pointers in 64-bit systems occupy 8 bytes. To convert a pointer to an unsigned integer type you need to use type specification uintptr_t defined in header <stdint.h>. As a result the function printf is just even unable to access all 8 bytes using the convefrsion specification %x.

Instead try the following using conversion specifier p specially designed to output pointers

printf("%d %x %p\n",lsc,lsc, ( void * )&lsc);

An alternative approach is to include header <inttypes.h> and write

#include <inttypes.h>

//...

printf("%d %x %" PRIxPTR "\n",lsc,lsc, ( uintptr_t )( void * )&lsc);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
-1

As other answers have explained, you are misusing printf() by not properly type matching the other arguments with the format. The other answers don't quite say so, but it is relevant that this causes the program to exhibit undefined behavior. The C language specifications therefore provide no answer about why that erroneous program behaves as it does. For the record, the correct printf call would be:

    printf("%d %x %p\n", lsc, (unsigned int) lsc, (void *) &lsc);

Inasmuch as you have added the information that adding -Wl,-mno-relax to the compilation command causes the program to emit the output you expect, we can suppose that this may be a facet of the UB elicited by misuse of printf. The -mno-relax disables an optimization that the linker can perform to transform an absolute addressing mode into a PC-relative mode, so it is thematically linked to the actual behavior difference you observe.

It may be that switching to the correct call, above, will give you the expected output regardless of -mno-relax. At the end of the day, however, your expectation that you can obtain a representation of an absolute address that way is not supported by the language spec. Where C equates pointer values with addresses, that does not require such values to be absolute addresses in a flat address space. Historically, there have been many implementations where sometimes (or always) it isn't. The only requirement on an address in the C sense is that it can be used to access the pointed-to object during the lifetime of that object.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • OP has already stated they tried `printf("%d %x %p\n", lsc, lsc, (void *)&lsc);`, so that is likely not the problem. – Eric Postpischil Jul 03 '23 at 12:23
  • @EricPostpischil, yes, I see that they said they tried it, but I don't see where they said whether it made any difference. In any case, that's not really where this answer hangs. – John Bollinger Jul 03 '23 at 12:26
  • Linker relaxation is not likely involved in why `%x` would not work but `%p` would. With either `%p` or `%x`, the compiler is going to calculate the address for `&lsc` and pass that as an argument. If it calculates the address correctly, `%p` would print it correctly, and `%x` would possibly cut it off (or print it as desired). If it calculates the address incorrectly, `%p` and `%x` would both print it incorrectly. It is more likely the address is out of range of what can be calculated using one immediate offset in an instruction, so `-mrelax` calculates the address incorrectly… – Eric Postpischil Jul 03 '23 at 12:29
  • … regardless of whether `%p` or `%x` is used and `-mnorelax` calculates it correctly, regardless of whether `%p` or `%x` is used. – Eric Postpischil Jul 03 '23 at 12:29
  • @EricPostpischil, the OP does not say that %p works as expected and %x not. In fact, you seem to acknowledge just the opposite yourself in your first comment. What I'm suggesting linker relaxation may actually be doing is making the stored value of the pointer *actually be* an offset, not an absolute address. – John Bollinger Jul 05 '23 at 05:19
  • OP edited the question 28 hours ago to state “Note: %p is not the issue, I have changed it to %x, its working in the same way listed above.” – Eric Postpischil Jul 05 '23 at 11:28
  • With the information so far, my expectation is the linker is breaking the addressing by the “relaxation” feature. I have not used this and do not have full information on it, but my expectation of how something like that ought to be designed is that it should not perform the relaxation transformation unless it does not break the address. Maybe that is not possible because the linker cannot know where the code will be loaded? If the linker does have the necessary information, I would expect this is either a bug in the linker or some error in how the build tools are being used;… – Eric Postpischil Jul 05 '23 at 11:53
  • … Maybe using linker relaxation requires support for some particular cooperation between the compiler and linker, and OP has not used matching tools with the necessary switches. If the linker does not have the necessary information, then how is linker relaxation supposed to work—is there an onus on the user to do something else to ensure it works? What? So resolving these issues is the direction I think this question and the answers ought to go in now. `%x` in `printf` is a red herring. – Eric Postpischil Jul 05 '23 at 11:55
  • @EricPostpischil, yes, it is exactly my point that linker relaxation is likely to be responsible for the observed effect. But my other point is that *that effect does not constitute breakage*. If you could show me a valid pointer that cannot be used to access its referent, or two pointers to the same object that do not compare equal, then that would be a different matter. But the example code does not demonstrate that, nor does it provide a sound basis to predict that such results could be demonstrated with different code. – John Bollinger Jul 05 '23 at 12:19
  • So your hypothesis is that linker relaxation has broken `%p` and that does not matter? The problem is, if linker relaxation has lost information, then multiple absolute addresses will map to the same “relaxed” address. Even assuming it is okay that the result of `%p` is insufficient to distinguish addresses, the linker does not know that the address is being passed to `printf` for `%p` so it is okay if it is wrong. The address could be passed for `%s` or to a routine other than `printf` that will use it to access an object. If the address is wrong for `%p`, then it is wrong for other uses too. – Eric Postpischil Jul 05 '23 at 12:29
  • No, @EricPostpischil, my hypothesis is that linker relaxation has produced unexpected, yet conforming, behavior. I'm pretty sure you can't show otherwise about the specific example program. Linker relaxation is exactly about switching from absolute addressing to relative addressing. It is viable to consider the offset used in the latter case to be an address in the C-language sense, and indeed, these are not unique. And since the linker needs some way to make decisions about which pointers can be relaxed for the feature to work at all, your claim that the linker does not have [...] – John Bollinger Jul 05 '23 at 13:15
  • [...] enough information to make the particular decision about whether a pointer argument to `printf` can be relaxed seems wholly speculative. And that supposes that it is even necessary to make that distinction at all. It could be the case, for example, that the pointer representation involved carries enough information to distinguish between absolute and relative addressing modes, and that the implementation provides a means for called functions to recover the absolute address at need, or even to avoid having such a need at all. – John Bollinger Jul 05 '23 at 13:20