Skip to main content
18 events
when toggle format what by license comment
Sep 14, 2023 at 1:46 history edited mtraceur CC BY-SA 4.0
couple typo fixes
Aug 3, 2022 at 6:51 history edited mtraceur CC BY-SA 4.0
Typo fix: s/as first/at first/
Feb 13, 2022 at 3:52 history edited mtraceur CC BY-SA 4.0
Clarify the difference between immediate vs computed/enregistered addresses/offsets
Feb 13, 2022 at 3:45 history edited mtraceur CC BY-SA 4.0
Explicitly mention absolute addresses rather than just "relative offsets" regarding moving existing instructions around
Feb 6, 2022 at 6:54 comment added mtraceur So I will edit this answer to mention calling conventions, and how knowing them and letting them shape our expectations can save some work, but I want to first carefully figure how I can work it into the answer while still drawing attention to risks like this.
Feb 6, 2022 at 6:53 comment added mtraceur Even I didn't notice this at first! @léo-lam Because I didn't quite have time to think it through, to load up all possibilities in my mind again, and so I thought "yep, that does sound right about the calling convention, and the optimizations I can think of were probably not happening in this case, so yeah, I probably can conclude those register writes in the call preamble must be safe to execute in this case". But we were subtly wrong, and at least in my case that was definitely because I allowed myself to think with the calling convention without the right caveats built into that thinking.
Feb 6, 2022 at 6:43 comment added mtraceur Basically, this isn't obvious from the snippet of machine code I included (unless you notice where the conditional branch instruction jumps and the orr w0, wzr, 0x1, and suspect that error is the error(3) function standardized in POSIX and so on), but that error call never returns, the compiler is fully justified in knowing that it never returns (in fact the corresponding C source could even contain something to tell the compiler that, such as a this-code-is-unreachable marker understood by the compiler, between the error call and the } that closes the if).
Feb 6, 2022 at 6:36 comment added mtraceur @léo-lam I agree, and after seeing your first comment I did form some intent to mention calling conventions. However, now we come to the bigger teachable moment: the machine code right after the call to error@plt had every right to ignore the effects of error on caller-saved registers. Because the call to error was the last thing inside an if block, with a compile-time constant nonzero error code, which means that branch is well-defined to never return on the Linux system this was targeting, and the compiler was within its rights to know this and use that knowledge.
Feb 5, 2022 at 21:55 comment added Léo Lam @mtraceur that's a fair point. In my experience looking at optimised C++ AArch64 code (mostly from Clang) and doing matching decompilation, functions that don't follow the standard calling convention are very rare though (at least in applications; kernels are a different story, of course) – so I think it's still worth mentioning that you can make your life easier in many cases if you know the platform ABI and know that it is being followed (and most of the time, it is, especially if the call goes through the PLT as in your example – that pretty much rules out weird optimisations).
Feb 5, 2022 at 17:27 comment added mtraceur @léo-lam probably, but that's a risky assumption. What if that specific machine code just wasn't following the ABI calling convention you're thinking of? Many compilers have a way to change the calling convention for specific functions, or a way to inline raw machine code which is free to violate it. Optimizations can become free to change anything about how registers are used if it is known that both the function and all of its callers are being optimized to match, which is possible for a leaf function in a command-line program like this.
Feb 4, 2022 at 22:54 comment added Léo Lam X0, X1, X2, X3 are caller-saved/volatile registers, so the compiler cannot assume anything about those registers after the call to error anyway (assuming error does not return a value in X0, of course). If the compiler needed to save the contents of X0, X1, etc. it'd have emitted code to load/restore those registers from the stack and/or used callee-saved registers. So in your specific example, patching those register moves is unnecessary.
Feb 26, 2021 at 21:57 history edited mtraceur CC BY-SA 4.0
fix a couple typos
Feb 12, 2020 at 16:42 comment added mtraceur @Abhishek Thanks, I've gone ahead and added a little extra hint in the preparation (step 0) section about checking if hexdump -R works, and if not then using an alternative like xxd instead of hexdump for all of the commands.
Feb 12, 2020 at 16:40 history edited mtraceur CC BY-SA 4.0
Disclaimer for `hexdump -R` portability, hint `xxd` instead.
Feb 12, 2020 at 16:20 comment added mtraceur @Abhishek Mine does. I see the problem - I only do this trick on my very minimal systems, which have the hexdump from BusyBox, which was compiled with FEATURE_HEXDUMP_REVERSE. I've never tried xxd -r because that is not available on the systems I do this kind of work on. (Either way, if objdump does not recognize the resulting binary, I would suspect that the binary is wrong, and something went wrong.)
Feb 12, 2020 at 12:53 comment added Abhishek hexdump does'nt recognize -R. Instead we can use xxd -r for reverse hex dumping, but beware that objdump does'nt recognize the reverse hex-dumped binary file.
Oct 1, 2018 at 9:25 history edited mtraceur CC BY-SA 4.0
elaborations/tweaks
Oct 1, 2018 at 9:14 history answered mtraceur CC BY-SA 4.0