Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements AT&T to Intel syntax conversion for x86 assembly in radare2. The implementation extracts and refactors the conversion logic into a reusable utility function, enabling assembly operations to automatically convert AT&T syntax to Intel when needed.
Key changes:
- Adds a new shared AT&T to Intel converter in
libr/util/str_att.cwith comprehensive instruction coverage - Refactors the existing
att2intelplugin to use the new shared implementation - Integrates automatic syntax conversion in the assembler when AT&T syntax is specified but encoders only support Intel
- Adds comprehensive test coverage with 35+ test cases covering various x86 instructions and edge cases
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
libr/util/str_att.c |
New implementation of AT&T to Intel syntax converter with instruction tables and conversion logic |
libr/arch/p/x86_nz/att2intel.c |
Refactored to delegate to the shared r_str_att2intel() function |
libr/asm/asm.c |
Integration of automatic syntax conversion in assembly process with arch-specific checks |
libr/include/r_util/r_str.h |
API declaration for r_str_att2intel() |
libr/include/r_asm.h |
API declaration for r_asm_att2intel() wrapper |
libr/include/r_arch.h |
New constants and bitmasks for encoder syntax support |
libr/util/meson.build |
Added str_att.c to meson build |
libr/util/Makefile |
Added str_att.o to makefile build |
test/db/cmd/attasm |
Comprehensive test suite with 35+ tests for AT&T syntax conversion |
test/db/cmd/cmd_asm_syntax |
Empty placeholder test file |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| while (start > buf && (isdigit ((unsigned char)*(start - 1)) || *(start - 1) == '-' || *(start - 1) == '+')) { | ||
| start--; | ||
| } | ||
| if (start < ptr && start > buf && *(start - 1) != ' ' && *(start - 1) != ',') { | ||
| // skip this case, it's part of an operand name | ||
| } else if (start < ptr) { | ||
| // there's a displacement | ||
| int disp = atoi (start); |
There was a problem hiding this comment.
The function uses 'isdigit' to detect displacement values, but this won't correctly handle hexadecimal values like '0x10' in AT&T syntax. When the code checks for digits before the bracket at line 252, it will only match decimal digits. For hex values like '0x10[rbp]', the 'x' character will stop the backward scan prematurely. Consider supporting hexadecimal displacement values or documenting this limitation.
| while (start > buf && (isdigit ((unsigned char)*(start - 1)) || *(start - 1) == '-' || *(start - 1) == '+')) { | |
| start--; | |
| } | |
| if (start < ptr && start > buf && *(start - 1) != ' ' && *(start - 1) != ',') { | |
| // skip this case, it's part of an operand name | |
| } else if (start < ptr) { | |
| // there's a displacement | |
| int disp = atoi (start); | |
| while (start > buf) { | |
| char c = *(start - 1); | |
| if (isdigit((unsigned char)c) || c == '-' || c == '+' || | |
| (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || | |
| c == 'x' || c == 'X') { | |
| start--; | |
| } else { | |
| break; | |
| } | |
| } | |
| if (start < ptr && start > buf && *(start - 1) != ' ' && *(start - 1) != ',') { | |
| // skip this case, it's part of an operand name | |
| } else if (start < ptr) { | |
| // there's a displacement | |
| int disp = (int)strtol(start, NULL, 0); |
| } | ||
|
|
||
| // skip directives and labels | ||
| if (*buf == '.' || (strlen (buf) > 0 && buf[strlen (buf) - 1] == ':')) { |
There was a problem hiding this comment.
When checking 'strlen(buf) > 0' at line 232, this is redundant since if '*buf' is checked first with the condition '*buf == '.'', then 'strlen(buf)' must be > 0. The condition could be simplified to just checking if 'buf[strlen(buf) - 1] == ':''. However, consider that calling 'strlen' twice (once for comparison, once for indexing) is inefficient. Store the length in a variable or use a pointer to check the last character.
| if (*buf == '.' || (strlen (buf) > 0 && buf[strlen (buf) - 1] == ':')) { | |
| size_t len = strlen (buf); | |
| if (*buf == '.' || (len > 0 && buf[len - 1] == ':')) { |
| if (argc >= 2 && (is_in_list (argv[0], ops_1) || is_in_list (op, ops_1))) { | ||
| // 1 operand instruction | ||
| if (newstr) { | ||
| snprintf (newstr, 256, "%s %s", op, argv[1]); |
There was a problem hiding this comment.
The function uses hardcoded buffer size 256 for snprintf calls, but allocates the buffer with strlen(att_str) + 256 on line 356. If the operands are long (especially memory addressing expressions), the snprintf calls could exceed 256 bytes. Consider using a consistent buffer size calculation or using the actual allocated size.
|
|
||
| // no operands fallback | ||
| if (argc == 1 && newstr) { | ||
| strcpy (newstr, op); |
There was a problem hiding this comment.
Using strcpy without bounds checking. Although 'op' is limited to 32 bytes and 'newstr' is allocated with strlen(att_str) + 256 on line 356, it's safer to use a bounds-checked copy function like strncpy or verify the size explicitly.
| if (is_in_list (argv[0], ops_0) || is_in_list (op, ops_0)) { | ||
| // 0 operand instruction | ||
| if (newstr) { | ||
| strcpy (newstr, op); |
There was a problem hiding this comment.
Using strcpy without bounds checking. Although 'op' is limited to 32 bytes and 'newstr' is allocated with strlen(att_str) + 256 on line 356, it's safer to use a bounds-checked copy function like strncpy or verify the size explicitly.
| // rebuild: [inner+disp]rest | ||
| size_t avail = strlen (att_str) + 64 - (start - buf); | ||
| snprintf (start, avail, "[%s%+d]%s", inner, disp, rest ? rest : ""); |
There was a problem hiding this comment.
If 'strdup(close + 1)' fails and returns NULL on line 269, the code continues and passes 'rest ? rest : ""' to snprintf. While this prevents a crash, if strdup failed due to memory pressure, the subsequent malloc on line 356 might also fail. Consider checking for allocation failures and returning early to avoid inconsistent state.
| // rebuild: [inner+disp]rest | |
| size_t avail = strlen (att_str) + 64 - (start - buf); | |
| snprintf (start, avail, "[%s%+d]%s", inner, disp, rest ? rest : ""); | |
| if (!rest) { | |
| free (buf); | |
| return NULL; | |
| } | |
| // rebuild: [inner+disp]rest | |
| size_t avail = strlen (att_str) + 64 - (start - buf); | |
| snprintf (start, avail, "[%s%+d]%s", inner, disp, rest); |
| if (argc >= 3 && (is_in_list (argv[0], ops_2swap) || is_in_list (op, ops_2swap))) { | ||
| // 2 operand instruction - swap operands (ATT: src, dst -> Intel: dst, src) | ||
| if (newstr) { | ||
| snprintf (newstr, 256, "%s %s, %s", op, argv[2], argv[1]); |
There was a problem hiding this comment.
The function uses hardcoded buffer size 256 for snprintf calls, but allocates the buffer with strlen(att_str) + 256 on line 356. If the operands are long (especially memory addressing expressions), the snprintf calls could exceed 256 bytes. Consider using a consistent buffer size calculation or using the actual allocated size.
| snprintf (newstr, 256, "%s %s, %s", op, argv[2], argv[1]); | ||
| return true; | ||
| } | ||
|
|
||
| // single operand fallback | ||
| if (argc == 2 && newstr) { | ||
| snprintf (newstr, 256, "%s %s", op, argv[1]); |
There was a problem hiding this comment.
The function uses hardcoded buffer size 256 for snprintf calls, but allocates the buffer with strlen(att_str) + 256 on line 356. If the operands are long (especially memory addressing expressions), the snprintf calls could exceed 256 bytes. Consider using a consistent buffer size calculation or using the actual allocated size.
| size_t avail = strlen (att_str) + 64 - (start - buf); | ||
| snprintf (start, avail, "[%s%+d]%s", inner, disp, rest ? rest : ""); |
There was a problem hiding this comment.
The buffer size calculation is incorrect. 'avail' is computed as strlen(att_str) + 64 - (start - buf), but 'buf' may have been modified (characters replaced) so its length may be different from att_str. The available space should be based on the actual allocated size of 'buf' or the remaining space from 'start' to the end of 'buf'. This could lead to buffer overflow.
| @@ -0,0 +1 @@ | |||
|
|
|||
There was a problem hiding this comment.
This test file appears to be empty (contains only whitespace). If this is intentional as a placeholder, consider either removing it or adding a comment explaining its purpose. Empty test files can be confusing for maintainers.
| # This file is intentionally left empty as a placeholder for future tests. |
Description