Unexpected output from strtol - c

I'm trying to create a small program that takes in a physical memory location and prints the data stored at the location. I'm passing two parameters into the program - the address, and the size (in bytes) of memory I want to print.
The issue I'm having is when the address I pass in gets above a certain value the strtol() function passes out a nonsensical value. Code below:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <asm-generic/fcntl.h>
#include <unistd.h>
int main(int argc, char** argv)
{
unsigned int mem_address,mem_size;
int loop, i;
int *ptr, *mem_address_current;
printf("mem_addr: %s\n",argv[1]);
printf("mem_size: %s\n",argv[2]);
mem_address = strtol(argv[1], NULL, 16);
mem_size = strtol(argv[2], NULL, 16);
printf("mem_addr: %x\n",mem_address);
printf("mem_size: %x\n",mem_size);
int mem_dev = open("/dev/mem", O_RDWR);
if(mem_dev == -1)
{
printf("No worky\n");
exit(1);
}
int alloc_mem_size, page_mask, page_size;
void *mem_pointer, *virt_addr;
page_size = sysconf(_SC_PAGESIZE);
alloc_mem_size = (((mem_size / page_size) + 1) * page_size);
page_mask = (page_size - 1);
mem_pointer = mmap(NULL,
alloc_mem_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
mem_dev,
(mem_address & ~page_mask)
);
if(mem_pointer == MAP_FAILED)
{
printf("no_worky2\n");
exit(1) ;
}
virt_addr = (mem_pointer + (mem_address & page_mask));
ptr = mem_pointer;
loop = (mem_size/16) + 1;
for(i = 0;i < loop;i++) {
printf("%#x: %08x %08x %08x %08x\n", ptr, *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3));
ptr = ptr + 4;
}
return 0;
}
If I run the the following command, I get the expected values.
root#p1025:~# ./test_prog_ppc 0100000 16
mem_addr: 0100000
mem_size: 16
mem_addr: 100000
mem_size: 16
0x48001000: 38210020 4e800020 9421ffe0 7c0802a6
0x48001010: 2c050000 bf410008 7c7e1b78 90010024
However, If I change the input address the value changes from what is expected:
root#p1025:~# ./test_prog_ppc ffee0000 16
mem_addr: ffee0000
mem_size: 16
mem_addr: 7fffffff
mem_size: 16
Any ideas as to why this would happen?
Thanks for the help.

strtol() returns a (signed) long. Based on your input, you are trying to parse unsigned 32 bit numbers, so if long is 32 bits on your system, strtol() is going to go out of range. In this case, it will return LONG_MAX, which on your system appears to be 0x7FFFFFFF.
You should use strtoul() instead, and store the result in an unsigned long, rather than an unsigned int as you are now, since while long is guaranteed to be at least 32 bits, int is not.
In addition, you really ought to be passing the address of a real pointer as the second argument to strtol() and friends, and using it to check for errors. Right now, if either of argv[1] or argv[2] did not start with a number, you wouldn't be able to distinguish whether the input was bad, or whether the input was a legitimate zero.

This behaviour is documented, see strtol
RETURN VALUE
...
If the correct value is outside the range of representable values, {LONG_MIN}, {LONG_MAX}, {LLONG_MIN}, or {LLONG_MAX} shall be returned (according to the sign of the value), and errno set to [ERANGE].
Your return value is LONG_MAX and when you test errno, it should be ERANGE.
You could use one of strtoul, strtoll or strtoull, which all would be able to handle larger values. Although strtoul is restricted to unsigned long, so if you are on a 64 bit system, you should use one of the strto*ll functions.

Related

why itoa fuction returns 32 bits if the size of variable in 16 bit

size of short int is 2 bytes(16 bits) on my 64 bit processor and mingw compiler but when I convert short int variable to a binary string using itoa function
it returns string of 32 bits
#include<stdio.h>
int main(){
char buffer [50];
short int a=-2;
itoa(a,buffer,2); //converting a to binnary
printf("%s %d",buffer,sizeof(a));
}
Output
11111111111111111111111111111110 2
The answer is in understanding C's promotion of short datatypes (and char's, too!) to int's when those values are used as parameters passed to a function and understanding the consequences of sign extension.
This may be more understandable with a very simple example:
#include <stdio.h>
int main() {
printf( "%08X %08X\n", (unsigned)(-2), (unsigned short)(-2));
// Both are cast to 'unsigned' to avoid UB
return 0;
}
/* Prints:
FFFFFFFE 0000FFFE
*/
Both parameters to printf() were, as usual, promoted to 32 bit int's. The left hand value is -2 (decimal) in 32bit notation. By using the cast to specify the other parameter should not be subjected to sign extension, the printed value shows that it was treated as a 32 bit representation of the original 16 bit short.
itoa() is not available in my compiler for testing, but this should give the expected results
itoa( (unsigned short)a, buffer, 2 );
your problem is so simple , refer to itoa() manual , you will notice its prototype which is
char * itoa(int n, char * buffer, int radix);
so it takes an int that to be converted and you are passing a short int so it's converted from 2 byte width to 4 byte width , that's why it's printing a 32 bits.
to solve this problem :
you can simply shift left the array by 16 position by the following simple for loop :
for (int i = 0; i < 17; ++i) {
buffer[i] = buffer[i+16];
}
and it shall give the same result , here is edited version of your code:
#include<stdio.h>
#include <stdlib.h>
int main(){
char buffer [50];
short int a= -2;
itoa(a,buffer,2);
for (int i = 0; i < 17; ++i) {
buffer[i] = buffer[i+16];
}
printf("%s %d",buffer,sizeof(a));
}
and this is the output:
1111111111111110 2

How can I copy 4 letter ascii word to buffer in C?

I am trying to copy the word: 0x0FF0 to a buffer but unable to do so.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <time.h>
#include <linux/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
void print_bits(unsigned int x);
int main(int argc, char *argv[])
{
char buffer[512];
unsigned int init = 0x0FF0;
unsigned int * som = &init;
printf("print bits of som now: \n");
print_bits(init);
printf("\n");
memset(&buffer[0], 0, sizeof(buffer)); // reinitialize the buffer
memcpy(buffer, som, 4); // copy word to the buffer
printf("print bits of buffer[0] now: \n");
print_bits(buffer[0]);
printf("\n");
return 0;
}
void print_bits(unsigned int x)
{
int i;
for (i = 8 * sizeof(x)-17; i >= 0; i--) {
(x & (1 << i)) ? putchar('1') : putchar('0');
}
printf("\n");
}
this is the result I get in the console:
Why am I getting different values from the bit printing if I am using memcpy?
Don't know if it has something to do with big-little-endian but I am losing 4 bits of 1's here so in both of the methods it shouldn't happen.
When you call
print_bits(buffer[0]);
you're taking just one byte out of the buffer, converting it to unsigned int, and passing that to the function. The other bytes in buffer are ignored.
You are mixing up types and relying on specific settings of your architecture/platform; This already breaks your existing code, and it may get even more harmful once you compile with different settings.
Your buffer is of type char[512], while your init is of type unsigned int.
First, it depends on the settings whether char is signed or unsigned char. This is actually relevant, since it influences how a char-value is promoted to an unsigned int-value. See the following code that demonstrated the difference using explicitly signed and unsigned chars:
signed char c = 0xF0;
unsigned char uc = c;
unsigned int ui_from_c = c;
unsigned int ui_from_uc = uc;
printf("Singned char c:%hhd; Unsigned char uc:%hhu; ui_from_c:%u ui_from_uc:%u\n", c, uc, ui_from_c,ui_from_uc);
// output: Singned char c:-16; Unsigned char uc:240; ui_from_c:4294967280 ui_from_uc:240
Second, int may be represented by 4 or by 8 bytes (which can hold a "word"), yet char will typically be 1 byte and can therefore not hold a "word" of 16 bit.
Third, architectures can be big endian or little endian, and this influences where a constant like 0x0FF0, which requires 2 bytes, would actually be located in a 4 or 8 byte integral representation.
So it is for sure that buffer[0] selects just a portion of that what you think it does, the portion might get promoted in the wrong way to an unsigned int, and it might even be a portion completely out of the 0x0FF0-literal.
I'd suggest to use fixed-width integral values representing exactly a word throughout:
#include <stdio.h>
#include <stdint.h>
void print_bits(uint16_t x);
int main(int argc, char *argv[])
{
uint16_t buffer[512];
uint16_t init = 0x0FF0;
uint16_t * som = &init;
printf("print bits of som now: \n");
print_bits(init);
printf("\n");
memset(buffer, 0, sizeof(buffer)); // reinitialize the buffer
memcpy(buffer, som, sizeof(*som)); // copy word to the buffer
printf("print bits of buffer[0] now: \n");
print_bits(buffer[0]);
printf("\n");
return 0;
}
void print_bits(uint16_t x)
{
int i;
for (i = 8 * sizeof(x); i >= 0; i--) {
(x & (1 << i)) ? putchar('1') : putchar('0');
}
printf("\n");
}
You are not writing the bytes "0F F0" to the buffer. You are writing whatever bytes your platform uses internally to store the number 0x0FF0. There is no reason these need to be the same.
When you write 0x0FF0 in C, that means, roughly, "whatever my implementation uses to encode the number four thousand eighty". That might be the byte string 0F, F0. But it might not be.
I mean, how weird would it be if unsigned int init = 0x0FF0; and unsigned int init = 4080; would do the same thing on some platforms and different things on others? But surely not all platforms store the number 4,080 using the byte string "0F F0".
For example, I might store the number ten as "10" or "ten" or any number of other ways. It's unreasonable for you to expect "ten", "10", or any other particular byte sequence to appear in memory just because you stored the number ten unless you do happen to specifically know how your platform stores the number ten. Given that you asked this question, you don't know that.
Also, you are only printing the value of buffer[0], which is a single character. So it couldn't possibly hold any version of 0x0FF0.

How to cast hex value to WORD, DWORD or QWORD and store the result in a double variable?

I'm trying to cast a signed hex number to WORD, DWORD and QWORD by this way:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main(void) {
printf("WORD=%d\n", (int16_t) strtol("F123", NULL, 16));
printf("DWORD=%d\n", (int32_t) strtol("FFFFF123", NULL, 16));
printf("QWORD=%lld\n", (int64_t) strtol("FFFFFFFFFFFFF123", NULL, 16));
return 0;
}
But it returns the following:
WORD=-3805
DWORD=2147483647
QWORD=2147483647
http://ideone.com/mqjldk
But why the DWORD and QWORD castings are not returning -3805 too?
I mean: 0xFFFFF123 stored in a DWORD would contain -3805 value in decimal, not 2147483647
Expected output:
WORD=-3805
DWORD=-3805
QWORD=-3805
Do you have an bitwise alternative to do it?
0xFFFFF123 is out of the range of a long int if a long int have 32 bit, so strtol() return LONG_MAX (0x7FFFFFFF = 2147483647 in our case).
use strtoull() to convert a string to a unsigned integer with at least 64 bits, and allways check for errors before proceed.
For print a integer with a specified bit size, use something like this:
printf("foo=%"PRIu32"\n",(uint32_t) foo);
a better way:
#include <stdio.h>
#include <stdlib.h>
#define __STDC_FORMAT_MACROS //we need that for PRI[u]8/16/32 format strings
#include <inttypes.h>
#include <errno.h>
void error_exit(void)
{
perror("ups");
exit(EXIT_FAILURE);
}
int main(void)
{
unsigned long long temp;
errno=0;
temp = strtoull("FFFFF123", NULL, 16);
if(errno)
{
error_exit();
}
printf("DWORD=%"PRId32"\n", (int32_t) temp );
errno=0;
temp = strtoull("FFFFFFFFFFFFF123", NULL, 16);
if(errno)
{
error_exit();
}
printf("QWORD=%"PRId64"\n", (int64_t) temp );
return EXIT_SUCCESS;
}
strtol does not assume two's complement input. In order for it to treat something as negative, you must use a minus sign. For example "-F123". This is the reason why the 2nd and 3rd line don't give negative output.
In case of the first line, you got the expected output mostly by accident. Because after the strtol call, you casted the hex value 0xF123 down to int16_t. It will not fit inside a int16_t, so it gets converted to a negative value.
Some bugs:
strtol("FFFFFFFFFFFFF123") will not work if long cannot hold the result. You should be using strtoll.
To print the stdint.h types, use the format specifier PRId from inttypes.h, example: printf("WORD=%" PRId16 "\n", my_int16_t);
Overall, avoid integer overflow of signed numbers. If you expect the input to fit inside an unsigned 32 variable but not a signed one, you should be using strtoul etc functions and after that convert to the signed type.
There are some errors inside this code.
The main issue in the first line is that you cannot cast a long int to an int16_t because int16_t is smaller in size than a long int.
In addition you should use the PRId16 specifier to print an int16_t.
The second line results in printing LONG_MAX as 0xFFFFF123 is out of range for a long int.
The third line has the same issue as the second, FFFFFFFFFFFFF123 is even more out of range and casting it to a 64bit values not help.
In addition you need to print it with %lld or PRId64 as Michael already stated.
The problem is that the maximum input for strtolis 0x7fffffff (or LONG_MAX). If the number to convert is out of range, errno is set and the result is LONG_MAX or LONG_MIN, see the strtol documentation for more details.
Illustration:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main() {
int x = strtol("7f000000", NULL, 16); // in range
printf ("x = %d, errno = %d\n", errno);
x = strtol("F0000000", NULL, 16); // out of range
printf ("x = %d, errno = %d\n", errno);
return 0;
}
Output:
x = 2130706432, errno = 0
x = 2147483647, errno = 34

Convert FFFFFF to decimal value (C language)

I am trying to convert a string representing a 24-bit hexadecimal number (FFFFFF) to its decimal equivalent (-1). Could anyone help me understand why the following code does not return -1?
Thanks, LC
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char temp_str[] = "FFFFFF";
long value;
value = strtol(temp_str, NULL, 16);
printf("value is %ld\n", value);
}
It seems like your input is the 24-bit 2's complement representation of the number, but strtol does not handle negative numbers in this way (and even if it did, it has no way of knowing that you meant a 24-bit representation). It only determines the sign of its output based on the existence of a - sign.
You can modify your code to get the result you want by adding this after the strtol:
if (value > 0x7fffff)
value -= 0x1000000;
Of course, this will only work for a 24-bit representation, other sizes will need different constants.
Hacker's delight covers this under sign extension.
For your 24 bit number, the sign bit is the 24th bit from the right and if it was set the hex value would be 0x800000.
The book suggests these:
((x + 0x800000) & 0xFFFFFF) - 0x800000
or
((x & 0xFFFFFF) xor 0x800000) - 0x800000
From your question I would say that your number is never going to be more than 24 bits so I would use the second option in your code as follows:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char temp_str[] = "FFFFFF";
long value;
value = strtol(temp_str, NULL, 16);
value = (value ^ 0x800000) - 0x800000; // Notice that I'm not using the & 0xFFFFFF since I assumed that the number won't have more than 24 bits.
printf("value is %ld\n", value);
}
Edit 1:
I fear that my original answer, though technically sound did not answer the posed question.
Could anyone help me understand why the following code does not return -1?
Others have already covered this by the time I answered but I will restate it here anyway.
Your string is "FFFFFF", it consists of 6 hex digits. Each hex digit represents 4 bits, therefore your string represents a 24 bit number.
Your variable long value is of type long which normally corresponds to your CPU's word width (32bit or 64bit). Since these days long can be either 32 bits or 64 bits depending on your architecture you are not guaranteed to get -1 unless you give exactly the right number of hex digits.
If long on your machine is 32 bits then two things are true:
sizeof(long) will return 4
Using "FFFFFFFF" will return -1
If long on your machine is 64 bits then two things are true:
sizeof(long) will return 8
Using "FFFFFFFFFFFFFFFF" will return -1
Digression
This then lead me down a completely different path. We can generalize this and make a program that constructs a string for your machine, such that it will always return -1 from a string.
#include #include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const char* ff = "FF";
char temp[sizeof(long) * 2 + 1]; // Ensure that the string can store enough hex digits so that we can populate the entire width of long. Include 1 byte for '\0'
int i;
long value;
/* Fill the temp array with FF */
for (i = 0; i < sizeof(long); ++i)
{
strcpy(&temp[i * 2], ff);
}
value = strtol(temp, NULL, 16);
printf("value of %s is %ld\n", temp, value);
}
This is a bad way to get a -1 result since the clear option is to just use
long value = -1;
but I will assume that this was simply an academic exercise.
Don't think as a computer now, just convery (FFFFFF)16 to decimal use ordinary math thinking. This is not about two's complement negative notation.
Because you run this program on 32- or 64-bit machine, not 24-bit. 0xffffff is actually 0x00ffffff, which is 16777215 in decimal.
Hex representation of -1 is 0xffffffff or 0xffffffffffffffff.

Why does C print my hex values incorrectly?

So I'm a bit of a newbie to C and I am curious to figure out why I am getting this unusual behavior.
I am reading a file 16 bits at a time and just printing them out as follows.
#include <stdio.h>
#define endian(hex) (((hex & 0x00ff) << 8) + ((hex & 0xff00) >> 8))
int main(int argc, char *argv[])
{
const int SIZE = 2;
const int NMEMB = 1;
FILE *ifp; //input file pointe
FILE *ofp; // output file pointer
int i;
short hex;
for (i = 2; i < argc; i++)
{
// Reads the header and stores the bits
ifp = fopen(argv[i], "r");
if (!ifp) return 1;
while (fread(&hex, SIZE, NMEMB, ifp))
{
printf("\n%x", hex);
printf("\n%x", endian(hex)); // this prints what I expect
printf("\n%x", hex);
hex = endian(hex);
printf("\n%x", hex);
}
}
}
The results look something like this:
ffffdeca
cade // expected
ffffdeca
ffffcade
0
0 // expected
0
0
600
6 // expected
600
6
Can anyone explain to me why the last line in each block doesn't print the same value as the second?
The placeholder %x in the format string interprets the corresponding parameter as unsigned int.
To print the parameter as short, add a length modifier h to the placeholder:
printf("%hx", hex);
http://en.wikipedia.org/wiki/Printf_format_string#Format_placeholders
This is due to integer type-promotion.
Your shorts are being implicitly promoted to int. (which is 32-bits here) So these are sign-extension promotions in this case.
Therefore, your printf() is printing out the hexadecimal digits of the full 32-bit int.
When your short value is negative, the sign-extension will fill the top 16 bits with ones, thus you get ffffcade rather than cade.
The reason why this line:
printf("\n%x", endian(hex));
seems to work is because your macro is implicitly getting rid of the upper 16-bits.
You have implicitly declared hex as a signed value (to make it unsigned write unsigned short hex) so that any value over 0x8FFF is considered to be negative. When printf displays it as a 32-bit int value it is sign-extended with ones, causing the leading Fs. When you print the return value of endian before truncating it by assigning it to hex the full 32 bits are available and printed correctly.

Resources