Original URL: http://blog.chinaunix.net/uid-20742320-id-4744472.html
Today, when I cross-compile an application, I find it troublesome to porting to the Board (NFS is not supported) and want to debug on the PC now. So in the board compiled good, run, found that the specified page cannot open, thought is httpd confi some discrepancy, check the next, there is no error. On the occasion of helpless, running directly on the PC suddenly discovers the presence of Segmdefault. How did the Segmdefault come about? Viewing the target file through the file command is elf64 and readelf library file discovery is ELF32. So here's the article: one of our most recently completed projects is porting a large 32-bit application that supports 11 operating system platforms in a 64-bit environment, and that the program's source code exceeds 300,000 lines. Since this 32-bit program was developed in several parts a few years ago, it is very likely that the code was written by different developers. In view of this, we have reason to suspect that the type mismatch that caused the problem in 64-bit porting is likely to be introduced over the years with the addition and deletion of the program modules.
We ported this 32-bit program to the 64-bit platform to take advantage of the advanced aspects of 64-bit technology-supporting larger files, supporting larger memory, and 64-bit computing, and the general approach is a iterative process that keeps going back and forth on a number of detail issues, such as byte order, tuning compiler options, etc. And stop every now and then to see if the overall goal is met-to comply with ANSI standards and future portability of the source code. In the first step, we studied 64-bit system resources to fully understand the compiler options, memory model, and coding considerations for each of the 11 operating system platforms. As a starting point for all the work, we opened all compiler warnings on one of the platforms, made the first build, and carefully examined the build log information. With these initial builds, using the local debugger, and later using a tool such as Parasoft's insure++, we identified a development blueprint, followed by a clear and thorough list of source code catalogs, which are checked after each configuration build.
After the initial code modification, debugging, and lookup of the build log, there is enough information to sort the events that might be encountered in the real world to prioritize. After a successful program with all of the basic functions successfully passed our automated test case, the transplant has finally reached a turning point; In addition to testing the 64-bit functionality, this test includes backward compatibility testing. If you are porting a project with several different 64-bit platforms, it is likely to be tested on one by one, and once the program is running correctly on the first platform, test the next platform, and so on. However, we have found a very good way to work on all platforms at the same time, because:
• Each compiler provides different information in its warning message, and a careful look at several compiler-generated errors can help us locate the problem area.
• Errors are different on different platforms. The same problem, on another platform, looks like there is no sign that it is likely to cause the program to crash on the current platform.
The last thing to consider in this project is to plan ahead for the final release of the test phase. Because the recently modified code is shared by 32-bit and 64-bit platforms, it must be thoroughly tested on every 32-bit platform, which will take twice the test time and resources.
Cross-platform issues
There are many problems during the migration of 32-bit programs to multiple 64-bit operating system platforms, ranging from compiler warnings to read-write binary data and more. Fortunately, the compiler can help us identify most 64-bit porting issues, setting the compiler warning level to be the most stringent on all platforms, and paying more attention to warnings that indicate data truncation and assign 64-bit data to 32-bit data. In any case, setting the compiler warning to a stricter level will result in more than one warning message, most of which can be automatically resolved by the compiler itself; the main problem here is that it is likely that the most important warning messages are overwhelmed by a lot of secondary information, and there is no way to make a distinction. To solve this problem, we build at the same time on different platforms, because different compilers can give different levels of warning information, which will help us make a distinction to filter out useless information and find out what really needs to be fixed.
Some applications require access to binary data or files that are shared with 64-bit and 32-bit, in which case the binary format of long and pointer must be carefully examined, and the associated read-write functions may need to be modified to convert different sizes and handle large-endian and small-endian problems across multiple platforms. To get the correct machine byte order, larger data sizes in 64-bit programs require more byte swapping.
1th Floor
For example, a 32-bit long:
Big Endian (large byte order) = (B0, B1, B2, B3)
Convert to:
Little Endian (small byte order) = (B3, B2, B1, B0)
and a 64-bit long:
Big Endian (large byte order) = (B0, B1, B2, B3, B4, B5, B6, B7)
Convert to:
Little Endian (small byte order) = (B7, B6, B5, B4, B3, B2, B1, B0)
Most compilers can find types that do not match and can correct them during construction, which is correct for most simple assignments, such as parameters passed to a function. The real problem is that the mismatch between int, long, and pointer, the compiler ignores it during compilation, or the compiler makes assumptions during compilation, causing the mismatch to persist. The former involves pointer parameters and function pointers, while the latter is mainly about function prototypes.
Passing an int and a long pointer as a function parameter can also cause problems if the pointer is later dereferenced as a different, incompatible type. This is not a problem with 32-bit code, but because ING and long are not interchangeable. However, in 64-bit code, because of the scalability of pointers and builds, this situation will result in run-time errors, and most compilers assume that what you are doing is what you want to do, so you will silently release the issue unless you open more warning messages. This problem is usually only surfaced when the program is running.
For example, example 1 can be compiled in both Solaris and Aix (Frote7, VAC 6) 32-bit and 64-bit mode without any warning, however, the 64-bit version runtime outputs an incorrect value. In such a short example, the problem can easily be found, if it is in the larger code, it will be much more difficult. This type of problem can be hidden in real-world code, and most compilers cannot find them.
Example 1:
#include
#include
int Func1 (char *);
int main ()
{
Long ARG, ret;
arg = 247;
ret = FUNC1 ((char *) &arg);
printf ("%ld\n", ret);
return (0);
}
int Func1 (char * input)
{
int *tmp;
TMP = (int *) input;
return (*TMP);
}
When running on a small-endian machine as a 64-bit executable, example 1 looks fine, because the value of ARG is fully contained in the four least meaningful bytes of long. However, even on small-endian x86 machines, a 64-bit version generates an error during run time when the value of Arg exceeds four of the least meaningful bytes.
It is because of a function pointer that the compiler cannot get information to determine which function will be called, so it cannot correct or warn you about a possible type mismatch, and all function arguments and return types that are called through a particular function pointer are within this range. If you want to fundamentally correct this problem, you must provide a separate scheme for proper type conversion of parameters and return values at function calls.
The second issue concerns implicit function declarations, which are assumed by the compiler if you do not provide a prototype for each function called in the code. The compiler produces a similar warning message "implicit function declaration:assuming extern returning int" is usually irrelevant at 32-bit builds, but at 64-bit builds, this return value is an int hypothesis, When a function actually returns a long or a pointer (such as malloc), it can cause a real problem. To keep the compiler from making this assumption, you must ensure that all required system header files contain or provide function prototypes for external functions.
2nd Floor
Hidden issues
Of course, there are some problems that will not be easily discovered in the first place, for example, in a 64-bit program, long and pointer sizes are larger, with the resulting increase in the size of the structure that contains them. The arrangement of the structure elements determines how much space the structure will occupy, for example, a structure that contains an int followed by a long, which occupies 8 bytes in a 32-bit program, but a 64-bit program adds 4 bytes of padding data to the first element of the structure so that the second element is more naturally arranged on the boundary. See Figure 1:
Figure 1: How data is arranged in 32-bit and 64-bit structures
To minimize the impact of populating data, the data elements in the structure are rearranged from large to small. However, if the data element is accessed through a byte stream, you must also adjust the logical part of the code to accommodate the new data arrangement in the structure.
In cases where rearranging the structure data does not work, or if the data element is accessed through a byte stream, it is necessary to carefully calculate the fill data, and our solution to this is to implement a helper function that removes the extra padding data from the structure before writing the data to the byte stream, and the other benefit is that There is no need to make any changes when reading the data, see Example 2:
Example 2:
typdef struct demo{
int i;
Long J;
} DEMO;
DEMO test;
/*pout_raw output raw data to a file */
/* Output Each element of the structure and avoid populating the data */
Pout_raw (int) file_unit, (char *) test.i, sizeof (TEST.I));
Pout_raw (int) file_unit, (char *) TEST.J, sizeof (TEST.J));
/* The downstream contains the fill data */
Pout_raw ((int) file_unit, (char *) test,sizeof (test));
Array problems
Arrays in long arrays or structs on 64-bit, not just those 32-bit peers, contain larger values and can contain more elements. Look back at the 4-byte variables previously used to define array boundaries and allocate array sizes, which can be converted to long. If you need to determine whether an existing long array should be converted to an int type in order for the 64 program to perform better, please refer to.
3rd Floor
Coding conventions and porting considerations
In addition to following the standard 64-bit coding conventions recommended by the operating system compiler documentation, there are a few comments and tips that may be helpful when planning a 64-bit migration:
• If possible and realistic, convert the source code to ANSI C + +. This will simplify the 64-bit porting process, and even future transplants will benefit.
• Does your target operating system support both 32-bit and 64-bit applications? The answer should be known early, as it will have an impact on the migration. For example: On Solaris, use the system command Isainfo to check the compatibility of 32-bit and 64-bit programs.
% Isainfo-v
64-bit sparcv9applications
32-bit SPARC Applications
• If your source code is already managed by a version control system such as CVS, it will help implement the first program before porting. Because we often need to make a lot of changes to the global code in order to migrate, and sometimes need to return to the previous version of the Code, the version control system is just the expert.
• Does your application use or load a 32-bit third-party library? If so, it is best to decide during the planning period whether these libraries should also be upgraded to 64-bit. If long and pointers cannot be passed between your main program and a third-party library, there is no need to migrate to 64-bit if the operating system can run both 32-bit and 64-bit programs, and if the operating system does not support it, plan to port the third-party program to 64-bit.
• If your program loads libraries dynamically at runtime and still uses old-fashioned calls to load (), use Dlopen () to correct data transfer problems between the main program and third-party modules, especially those older AIX programs that precede Dlopen (). To open a run-time link on AIX, the linker must add the-BRTL option with the-L ":" To specify the location of the library. To improve compatibility, your main program and all libraries loaded through Dlopen () must be recompiled using the run-time link.
• Consider backwards compatibility. Backward compatibility issues become particularly important when migrating to 64-bit platforms, and existing test scenarios must be improved to include old-fashioned 32-bit tests and modern 64-bit tests.
Tools
Compiling a detailed inventory of large source code, especially shared across 32-bit and 64-bit platforms, and evaluating every modification, no matter how trivial, is a daunting task, since ignoring the potential of conversion problems and the probability of introducing new errors are very high. However, with some 64-bit tools and techniques, many of these potential problems can be captured during the pre-compilation phase, the compile phase, and the runtime. Here are some of the tools available:
• Pre-compilation phase. Using the-ERRCHK=LONGPTR64 option in the compiler, you can enable lint, which is very effective for snapping type conversions, implicit function declarations, and parameter mismatches, example 3 demonstrates a typical lint warning, and of course there are other lint types of programs, such as Flexlint ().
• Compile-time tricks. Adjusts the compiler warning level to ensure that no warning messages are suppressed at least in the initial phase of the project. For multi-platform environment, on different operating systems, compiling the same source code, will have different problems, to make full use of this point, compare and analyze the problem.
• Compile-time and run-time tools. Advanced tools, such as insure++ or Purify for 64-bit, are targeted at at least one platform and are useful in any development environment for runtime and compile time.
• Run-time tools. Try DBX, which is provided by each UNIX compiler, or DDD (data display debugger), which is the graphics interface () for DBX and gdb on UNIX.
Example 3: Typical lint warning
warning:implicit function Declaration:main
Warning:argument does not match remembered Type:arg #1
warning:passing 64-bit integer arg, expecting 32-bit integer:
MyProc (Arg 7)
Warning:assignment of 64-bit integer to 32-bit integer
Warning:function argument (number) used inconsistently
Warning:comparing 32-bit integer with 64-bit integer
Conclusion
Take some time to do pre-planning and investigation, although not necessarily to achieve a multiplier effect, but from the results to be worth it. And don't be discouraged when no one in your program is working properly, check the code carefully and systematically and find out where the problem is. As the number of memory and data grows rapidly over the year, we have reason to believe that the difficult process of porting transforms is nothing compared to the benefits of 64-bit programs.
"Turn" running on 64-bit machine 32-bit program considerations